0

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.

Christopher Galpin
  • 1,088
  • 12
  • 27

1 Answers1

1

LightSwitch doesn't actually support one-to-one relationships, so even if you had been successful in your RIA service, it wouldn't have worked in LightSwitch anyway.

The work-around is to create a relationship with a multiplicity of zero-to-many, or one-to-many (as you ended up doing). You have to "enforce" the one-to-one in your validation, or code.

Yann Duran
  • 3,842
  • 1
  • 24
  • 24
  • I don't understand why but the problem went away. Now I can access "ObjectContext.Dogs" after updating the model from a database with a one-to-one relationship just fine. I didn't notice until just now. – Christopher Galpin Apr 18 '13 at 17:05