0

I am using Entity Framework 4.0. I am facing problem while updating data in the database. I am getting this exception :

An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same key.

I am working on the relational database. The following are 3 entities and there relation :

         1    to   *        1   to    *
Ceremony -----------> Menu ------------> CourseOption

Everything works fine, if I update the ceremony which already contains the Menus and CourseOptions. The problem comes when we insert new Menu and CourseOption entry in the ceremony at the time of update. Than I got the above mention exception.

C# code to update existing ceremony

public HttpResponseMessage PutCeremony(int id, Ceremony ceremony)
{
    if (ModelState.IsValid && id == ceremony.Id)
    {
       db.Entry(ceremony).State = EntityState.Modified;

       try
       {
           db.SaveChanges();
       }
       catch (DbUpdateConcurrencyException)
       {
           return Request.CreateResponse(HttpStatusCode.NotFound);
       }

       return Request.CreateResponse(HttpStatusCode.OK, ceremony);
    }
    else
    {
       return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
}

How can I get rid of this problem? Please help

EDIT

Today i spend a whole day in this, i read a lot of articals and stackoverflow question. I found that once a product is fetched from the Context, the context is keeping track of it so that why instead of using :

db.Entry(ceremony).State = EntityState.Modified;

I used

db.Entry(db.Ceremonies.Find(ceremony.Id)).CurrentValues.SetValues(ceremony);

Now by doing this change the exception gone and Ceremony entity properties are changing properly. But the ceremony related Menus entry and CourseOptions entry are not updating. Please give me suggestion guys. I am absolutely new in EF.

Tom Rider
  • 2,747
  • 7
  • 42
  • 65
  • hey guys, the question is too difficult Or it is not clear, if later is the case than please let me know – Tom Rider Oct 29 '12 at 10:34
  • 2
    You must change the question name!! – gillyb Oct 29 '12 at 10:57
  • So, when you create a new `Ceremony`, you add new `Menu`s and `CourseOtion`s, or are they preexisting? – Heki Oct 29 '12 at 11:00
  • yes, at the time of creating new ceremony user can add Menu and course options, but if user want to add these (Menu and courseoption) later that he can do this while editing the ceremony. But the problem occure when we add new menu at the time of updating the existing ceremony – Tom Rider Oct 29 '12 at 11:03
  • So it's not failing when you create a new `Ceremony`? It's actually impossible to see what's happening to `ceremony` before it's passed to `PutCeremony`. What I'm fishing for is what happens where you add those `Menu`'s. – Heki Oct 29 '12 at 11:04
  • Yes when i add new ceremony , than it adds properly without any issue, probelm comes only at the time of updating the existing ceremony – Tom Rider Oct 29 '12 at 11:06
  • If you take a `Ceremony` from `db` and update it (let's say it has a `Name`) with a change in `Name` and store that change, do you then get in trouble? Work from the simplest and make your way toward the code you want. – Heki Oct 29 '12 at 11:08
  • No if i update the Ceremony entity properties than there is no trouble with that, trouble occure when i change the Menus property array (Navigation property to Menu entity). If i add new Menu object Or delete any existing that the changes do not take place in the database – Tom Rider Oct 29 '12 at 11:12
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/18713/discussion-between-tom-rider-and-heki) – Tom Rider Oct 29 '12 at 11:14

1 Answers1

1

Updating a detached object graph can be very difficult. (An example with only one child collection is here: https://stackoverflow.com/a/5540956/270591 If you have a grand child collection it is even more complex.)

There is no generic approach or a simple method like setting the state to Modified (it only marks scalar properties as changed) and hoping it would store all the object graph changes.

The details to consider to implement such an update:

  • Does the user create new menus when a menu is added to the ceremony or does he only establish a relationship between the ceremony and an existing menu?
  • Does the user delete an existing menu when a menu is removed from the ceremony or does he only release a relationship between the ceremony and the menu, but the menu should be still existing in the database (the FK must be nullable then)?
  • Can the user change (scalar) menu properties in your view or can he only add menus to a ceremony or remove them without changing the menus themselves?
  • The same questions apply to the grandchild collection CourseOptions of each Menu.

For the (relatively simple) case that a user in your specific ceremony Edit view...

  • can modify scalar properties of the ceremony
  • can remove existing menus from a ceremony that should be deleted from the database when they are removed and the related CourseOptions should be deleted as well and cascading delete for that relationship is enabled in the database
  • can create and add new menus to a ceremony that should be inserted into the database
  • cannot change scalar properties of existing menus
  • cannot add CourseOptions to an existing Menu and cannot delete them from a Menu
  • can add new CourseOptions to an new Menu (and the CourseOptions should be inserted together with the new menu)
  • cannot change scalar properties of an existing CourseOption

...the code would look like this:

var ceremonyInDb = db.Ceremonies.Include(c => c.Menus)
    .Single(c => c.Id == ceremony.Id);

db.Entry(ceremonyInDb).CurrentValues.SetValues(ceremony);

foreach (var menuInDb in ceremonyInDb.Menus.ToList())
    if (!ceremony.Menus.Any(m => m.Id == menuInDb.Id))
        db.Menus.Remove(menuInDb);

foreach (var menu in ceremony.Menus)
    if (!ceremonyInDb.Menus.Any(m => m.Id == menu.Id))
        ceremonyInDb.Menus.Add(menu);

db.SaveChanges();

If some of the restrictions do not apply (i.e. the user can do more complex modifications in your view) the code will be more complex. But the basic idea is to load the object graph (root and children with Include and possibly grand children with Include(...Select(...))) from the database, compare that original graph with your new detached graph and apply changes to the original graph according to the differences to the new graph.

Community
  • 1
  • 1
Slauma
  • 175,098
  • 59
  • 401
  • 420