0

Let's assume that the below method lives in a WCF service. The UI retrieved an instance of the Status object, and makes a subsequent call to the service using this method. Instead of assigning the status to the user as I would expect, it attempts to insert the status. What am I doing wrong?

void Method(Status status)
{
    //not sure if this is even needed, the status never changed
    context.Statuses.ApplyChanges(status);

    //get the first user from the database
    User user = context.Users.Where(u => u.Id = 1).First();

    //set user status to some existing status
    user.Status = status;

    //this throws an exception due to EF trying to insert a new entry
    //into the status table, rather than updating the user.StatusId column.
    context.SaveChanges();
}
e36M3
  • 5,952
  • 6
  • 36
  • 47
  • Yes, schema wise User has a foreign key User.StatusId, hence the assignment of user.Status = status; – e36M3 Apr 11 '11 at 19:51
  • I'm assuming the code you posted isn't the real code, as your retrieval code isn't actually running the query. You need to add `.First()` (or `Single()` or whatever you desire) after the where clause to actually retrieve the user instance. – KallDrexx Apr 11 '11 at 20:13
  • Sorry about that, that's what I get for typing up an example in notepad. Thanks KallDrexx. – e36M3 Apr 11 '11 at 20:15

3 Answers3

1

Try this instead:

        using (Entities ctx = new Entities())
        {
            ctx.Statuses.Attach(status);

            ObjectStateEntry entry = ctx.ObjectStateManager.GetObjectStateEntry(status);
            entry.ChangeState(EntityState.Modified);

            //get the first user from the database
            User user = ctx.Users.Where(u => u.Id = 1);

            //set user status to some existing status
            user.StatusID = status.StatusID;

            ctx.SaveChanges();
        }

Here's a tutorial on CRUD with Entity Framework if you're interested.

BrandonZeider
  • 8,014
  • 2
  • 23
  • 20
  • BrandonZeider, but why would I have to set it to modified if I never in fact modified the Status object? Logically I want EF to know that this object was in fact retrieved prior and is NOT modified, hence doesn't need any sort of update. – e36M3 Apr 11 '11 at 19:52
  • Also, why would I need to attach the entities, isn't that the whole point of STEs is that I don't need to manually attach them and set their state? – e36M3 Apr 11 '11 at 19:56
  • Ah, then you don't need to mess with it at all. Just get your user, set the statusID to status.StatusID and call SaveChanges(). You only need to change the entity state to modified if you want to save the changes to the status entity. I was thrown off by your call to ApplyChanges()... – BrandonZeider Apr 11 '11 at 20:01
  • I don't understand why that should make a difference. I know what you are suggesting should work, but it still doesn't explain to me why I am getting inserts instead of updates. – e36M3 Apr 11 '11 at 20:17
  • My guess is because you set user.Status equal to an unattached status object. Try attaching status first and see if it inserts a new record. If not, then that's what was going on. – BrandonZeider Apr 11 '11 at 20:20
  • By definition self tracking entities do not need to be attached, because ApplyChanges will indirectly do that for you :) Something else is wrong with what I'm trying to do. – e36M3 Apr 11 '11 at 20:28
1

The problem is that you are working with attached user. When the STE is attached to the context it behaves exactly in the same way as any other entity. More over its self tracking mechanism is not activated. So you must attach the status to the context before you set it to the user or it will be tracked as a new entity which has to be inserted:

void Method(Status status)
{
    User user = context.Users.Where(u => u.Id = 1).First();

    context.Attach(status);
    user.Status = status;

    context.SaveChanges();
}
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
0

Have to write an answer because I can't yet comment on another answer (rep score < 50) [something weirdly not right about that, but I get why it's like that] because I wanted to add some clarity to @Ladislav's answer.

The Status object coming in from the WCF call did not come from the same context you are using to find the User object, so the tracking code is not fixed up with that context. This is why attaching it will allow you to save the assignment without the context thinking that status is a new entity requiring an insert into the database.

eudaimos
  • 665
  • 7
  • 11
  • I understand what you're saying, but I always thought that ApplyChanges() with STEs also attaches the entity to the context, maybe I'm wrong. – e36M3 May 05 '11 at 16:35