views:

364

answers:

2

EDIT: Stub Entities Definition

I have two entity types Subscriber and AddOn

Their definition in data model is as below

Subscriber(#SubscriberID,Name,AddOnID)

AddOn(#AddOnID,Name)

AddOnID column in the Subscriber table references the AddOnID column in AddOn table.


I'm trying to update the AddOn reference of a specific Subscriber entity. Lets say, i want to change the Subscriber#1's AddOn reference to AddOn#5. Here's the code:

    Subscriber subscriber = new Subscriber { SubscriberID = 1};
    AddOn newAddOn = new AddOn { AddOnID = 5};

    using (var context = new TestEntities())
    {
        context.AttachTo("AddOn", newAddOn);
        context.AttachTo("Subscriber", subscriber);

        subscriber.Name = "dummy";
        subscriber.AddOn = newAddOn;

        context.SaveChanges();
    }

This throws an exception in line "context.SaveChanges();"

A relationship is being added or deleted from an AssociationSet 'FK-Subscriber-AddOn'. With cardinality constraints, a corresponding 'Subscriber' must also be added or deleted.

When I comment out the "subscriber.AddOn = newAddOn;" line, the update operation works just fine.

So, why i can't just update the reference property like as i update the non-reference one?


Note: I don't know if it is the right way but adding a "context.Refresh(RefreshMode.StoreWins,subscriber);" OR "context.Refresh(RefreshMode.ClietWins,subscriber);" after attach statements make things work.

Why is this behaviour?

A: 

I think you are asking how to add a child entity to another entity. So, in your case you have the Parent Entity: Subscriber and a child entity: AddOn. If this is the case, and your data model has the correct relationship setup then you will probably want code like:

Subscriber subscriber = new Subscriber { SubscriberID = 1};
AddOn newAddOn = new AddOn { AddOnID = 5};

using (var context = new TestEntities())
{

    context.AttachTo("Subscriber", subscriber);
    subscriber.Addon.Add(newAddOn);

    subscriber.Name = "dummy";

    context.SaveChanges();
}
Neil Kimber
nope i wasn't asking about adding. since subscriber-addon has a many-to-one relationship, every subscriber that exists in the datasource must have an addon defined. so what i need was a foreign key update.
Cankut
+2  A: 

In EF 3.5 SP1 you can't modify a reference (i.e. subscriber.Addon) without knowing the original value. Note this restriction will go away in EF 4 if you use FK Associations.

Now most of the time the EF hides this extra complexity from you, but not when you are using Attach like this.

Here is the code you need:

AddOn newAddOn = new AddOn { AddOnID = 5};
AddOn oldAddOn = new AddOn { AddOnID = 4}; // you need to remember the old id.
Subscriber subscriber = new Subscriber { SubscriberID = 1, AddOn = oldAddOn};

using (var context = new TestEntities())
{
    context.AttachTo("AddOn", newAddOn);
    context.AttachTo("Subscriber", subscriber); // will attach the oldAddOn too

    subscriber.Name = "dummy";
    subscriber.AddOn = newAddOn;

    context.SaveChanges();
}

As you can see we are simply telling the EF about the original relationship, and then changing it as before.

That should fix your problem.

As you discovered calling refresh also works... because it grabs the original value for the reference from the database for you. The downside of using Refresh is it issues a query back to the database.

So attaching the original value like above simply saves that extra query.

Hope this helps

-Alex

Program Manager Entity Framework Team.

See Tip 26 of my EF Tips series for more information.

Alex James
In my case, the only thing i know is the id of Subscriber, i've no information about the older AddOnID.So there's no solution(with no extra query) to this situation in EF 3.5 right?
Cankut
Yes you are right.
Alex James
If you didn't know the old id could you use the pattern that Alex has put up but make the oldAddOn have an id of something like -1. Then EF will see you referencing newAddOn with a different id... I'm not sure if it will work but I thought I would through it out there as a possibility...
vdh_ant