I've come a long way in my understanding of WCF RIA Services and Microsoft LightSwitch, but now I'm at a loss. I'll use Animal
and Dog
as examples. I actually want a zero-or-one-to-one relationship, not every Animal will necessarily be/have Dog.
First I create a one-to-one relationship in my database that I want to use with RIA Services. Then I update the Model.edmx file in my RIA Services project like usual. The issue is instead of this giving me access to ObjectContext.Dogs
as I was expecting, that doesn't exist, and instead Animal has been given the extra properties of Dog. Apparently one-to-one relationships are specially handled in this manner, which seems like it's intended to be helpful, but I'm having much difficulty working with it this way.
My RIA Services classes look like this:
public class RiaDog
{
[Key]
public int Id { get; set; }
public int PackLeaderId { get; set; }
[Association("RiaAnimal_RiaDog", "Id", "Id", IsForeignKey = true)]
public RiaAnimal Animal { get; set; }
}
public class RiaAnimal
{
[Key]
public int Id { get; set; }
[Association("RiaAnimal_RiaDog", "Id", "Id", IsForeignKey = false)]
public RiaDog Dog { get; set; }
}
Unless there's some sort of magic one-to-one data annotation attribute I'm not aware of, I have to write CRUD methods for RiaDog
just like RiaAnimal
and every other class. This gets pretty awkward given the fact that ObjectContext.Dogs doesn't exist, I have to work through Animal:
[Query(IsDefault = true)]
public IQueryable<RiaDog> GetAllDogs() {
var report = from animal in ObjectContext.Animals
where animal.PackLeader != null
select new RiaDog
{
Id = animal.Id,
PackLeaderId = animal.Dog.PackLeaderId,
};
return report;
}
Already this feels strange, I must use a where clause on one of the Dog properties Animal was given to isolate out the dogs.
With some simple creativity I can get the insert, update, and delete methods functional:
public void InsertDog(RiaDog riaDog) {
var animal = ObjectContext.Animals.FirstOrDefault(a => a.Id == riaDog.Id);
animal.PackLeader = ObjectContext.PackLeaders.FirstOrDefault(pl => pl.Id == riaDog.PackLeaderId);
riaDog.Animal.Dog = riaDog;
}
public void UpdateDog(RiaDog rd) {
var animal = ObjectContext.Animals.FirstOrDefault(a => a.Id == riaDog.Id);
animal.PackLeader = ObjectContext.PackLeaders.FirstOrDefault(pl => pl.Id == riaDog.PackLeaderId);
}
public void DeleteDog(RiaDog rd) {
}
In UpdateAnimal()
I use the backreference I set in InsertDog()
and add:
if (riaAnimal.Dog == null)
riaAnimal.PackLeader = null;
I also have Dog set to cascade delete with Animal on the database.
In the LightSwitch front-end I have this on the RiaDog entity:
partial void AnimalType_Changed() {
if (AnimalType != null && AnimalType.Name == "Dog" && this.Dog == null)
this.Dog == new RiaDog();
else if (this.Dog != null && (AnimalType == null || AnimalType.Name != "Dog"))
this.Dog = null;
}
The problem I'm having is if I change the type to Dog, save, change it back to something else, save, then change it to Dog again and save, I receive the error: The context is already tracking a different entity with the same resource Uri.
I've learned to handle other tracking errors:
When entities are created in the code-behind client-side, they need unique ids to be properly tracked client-side.
In any insert methods in WCF RIA Services, the entity's client id must be set to the real id after the database generates it, e.g.:
public void InsertRiaEntity(RiaEntity re) {
var entity = new Entity();
entity.SomeProperty = re.SomeProperty;
entity.AnotherProperty = re.AnotherProperty;
ObjectContext.Entities.AddObject(entity);
ObjectContext.SaveChanges();
// essential for client<->server tracking
re.Id = entity.Id;
}
However I don't know how to solve this current tracking error. Really I feel like I'm taking the wrong approach and I'm looking for an expert's advice on how you're supposed to code one-to-one or one-or-zero-to-one relationships in WCF RIA Services for Microsoft LightSwitch.
I've actually managed to get this all working with a one to many relationship, and in short and reasonable enough code that it seems "correct", so this variation should be achievable.