none
Update a detached entity in ADO .net entity framework

    Question

  • Hi,

    I have just started working on the ADO .Net Entity Framework and so far I like what I see.

    The only problem is that most of the usage scenarios that I can see have a very  short lived Entity Context. So the best idea seems to be that I should retrieve all the data in detached mode, send it to the presentation layer (through a webservice or directly) which acts on it and returns it the business layer for update.

    I seriously hate the fact that I have to either:

    1) Hit the database again to retrieve the old state of the entity, and then update it

    Code Snippet

    void Save(Products product)
    {
    using (NorthwindEFEntities entities = new NorthwindEFEntities())
                {
    entities.Attach(product);

    entities.ObjectStateManager.GetObjectStateEntry(product).SetModified();
    entities.Refresh(RefreshMode.ClientWins, product);
    entities.SaveChanges();
    }
    }

     

     

    2) Create a translator a set each property of the entity again at the BL to mark it as modified:

    Code Snippet

    void Save(Products product)
    {
    using (NorthwindEFEntities entities = new NorthwindEFEntities())
    {
    Products prod = new Products();
    prod.ProductId = product.ProductId;

    entities.AddToProducts(prod);

    entities.AcceptChanges();


    TranslateObject(product, ref prod); // for each property of product, prod is updated


    entities.SaveChanges();

    }

     

     

    3) Use the method described in the link http://blogs.msdn.com/deviations/archive/2008/10/21/how-to-apply-changes-made-to-a-detached-object-without-passing-it-or-retrieving-it-in-net-framework-3-5-ado-net-entity-framework.aspx

     

    As you can see, none of these methods really measure up. The first one can become a performance liability, the second one seems plain old stupid and the third one I cant get to work.

     

    What I want is the ability to mark an object as dirty without actually having to iterate through all its property and changing them or calling the DB.

     

    Can any one please help me is this regard (like offer a better solution or get the third method to work if possible??)

    Thanks

    Monday, November 17, 2008 10:45 AM

All replies

  •  

    in my case, none of your approaches work for me.

    when i try to attach i get an error "[System.InvalidOperationException] = {"An entity object cannot be referenced by multiple instances of IEntityChangeTracker."}"

    so, i guess its already attached right?

    even that way, entity is not updated....

    Monday, November 17, 2008 2:59 PM
  • No your problem hasnt progressed that far yet. You see, when you get an object through a context, it is registered to it so that the context can track all its changes. Even if the context has expired, the entity object still considers itself registered and cannot be registered to another context.

    To use an entity object in a context different from the one it was created from, you first have to detach it from the original context. This removes all the object's references from the
    IEntityChangeTracker and can now be referenced by some other IEntityChangeTracker instance like your new context
    Tuesday, November 18, 2008 8:07 AM
  • Has anyone come up with an acceptable way of dealing with this situation? I'm struggling with it myself.

    Prior to EF, I've been using LINQ-to-SQL in a similar detached manner, and at least with that all you needed to do was attach the object graph to the data context - under the covers it handled everything for you (okay, it was still hitting the database again but at least your BLL code was clean).

    I can't believe MS haven't learned their lesson from LINQ-to-SQL, and come up with a better solution for n-tier applications.


    Andy

    Thursday, December 18, 2008 10:25 AM
  •  This is most simple way, I guess:

    1. Create HashSet:

    HashSet<Employee> _changes = new HashSet<Employee>();


    2. Subscribe to PropertyChanged event when you detach object, for example:

    foreach (Employee emp in Employees)
    {
    emp.PropertyChanged +=
    new PropertyChangedEventHandler(emp_PropertyChanged);
    awe.Detach(emp);
    }

    3. in void emp_PropertyChanged add element to HashSet:

    if (!_changes.Contains(emp)) _changes.Add(emp);

    4. Examine HashSet when you attach objects to new ObjectContext:

    foreach (Employee emp in Employees)
    {
    awe.Attach(emp);

    if (_changes.Contains(emp))
    awe.ObjectStateManager.GetObjectStateEntry(emp.EntityKey).SetModified()
    ;
    }

    Probably you can find some pattern that suits better particular needs. Built-in solution doesn't exist.
    You might also want to check this: http://code.msdn.microsoft.com/entitybag



    File
    Saturday, December 20, 2008 4:53 PM
  • In addition to what Nenand proposes, you may want to use Danny's AttachAsModified extension method to mark the entity properties as dirty:

    http://blogs.msdn.com/dsimmons/archive/2008/10/31/attachasmodified-a-small-step-toward-simplifying-ef-n-tier-patterns.aspx

    Also, regarding detaching entities to remove the reference to the change tracker, there is an important distinction to make: when you detach, not only the entity is no longer tracked, but also the object graph is shredded: the detached entity  looses all references to related entities. When you serialize and deserialize an object graph across boundaries, for instance using WCF serialization, the graph that you obtain is no longer tracked, but it is still the full graph.
    This posting is provided "AS IS" with no warranties, and confers no rights.
    Saturday, December 20, 2008 7:43 PM
    Moderator
  • My solution thus far has been to drag out EFExtensions and do a good old fashioned stored procedure update of the object.  After reading around on this issue I guess I am sticking with that for now.
    • Proposed as answer by MvcCmsJon Thursday, June 18, 2009 7:10 PM
    Thursday, June 18, 2009 7:05 PM
  • The solution is none of the above, especially the responses by MSFT, as they are nothing more than hacks. The real solution is... wait for it...

    DON'T USE ENTITY FRAMEWORK, STICK TO LINQ TO SQL OR NHIBERNATE!!

    Whoever at MSFT made the decision to stop supporting Linq to SQL and to only go forward with EF should be fired. Even with .NET 4 EF, detached entities are still a royal pain in the butt to use and you find yourself doing hack after hack to try and do very basic things such as updating. Considering that in ASP.NET MVC when you submit a form it returns back the business object detached, you'd think they'd consider this an important thing and make sure they do it right. Instead it seems like another case of separate MSFT teams not having any clue what each other are doing and making it super hard to work together, just like the Silverlight and WCF teams.

    Enough venting, just stay away from EF, that's the only real solution...

    Friday, June 11, 2010 10:22 PM
  • Are you still having this issue in Entity Framework 4 (.Net Framework 4)?

    There have been significant improvements in working with detached objects in the lastest version.

    Friday, October 15, 2010 2:38 PM
  • Joshua,

    Do you know of any changes in EF4 that address the original poster's question?  I didn't use the previous versions so what exactly were these changes/improvements in the latest one with respect to what is being discussed in this thread?

    ShaneB

    Monday, October 25, 2010 5:43 PM
  • Well, previously (EF v1) when you detach your entities, you could not reattach them. You would have to create a secondary graph for comparison when you pass the original graph back to the server. Now, you can just reattach the original graph... I think.

    I had a real problem working with a detached graph previously. After .Net 4/EF 4 came out, I saw Tony Sneed give a presentation on the improvements of EF 4 and working with POCO. He assured me those issues have been resolved. I've only had two small EF projects since, so haven't gotten to test the robustness, but so far so good.

    You should check out Tony Sneed. He knows a lot about EF v4. http://blog.tonysneed.com/.

    I hope that helps.

    Wednesday, October 27, 2010 10:14 PM
  • His scenario one does not apply changes to object in the graph and all columns are updated not just the ones that were actually changed unfortunately. Ugg I'm so tired of this BS. I cant believe MS has made working with detached entities so difficult.
    Thursday, October 28, 2010 4:27 PM
  • I haven't really had a problem with applying changes. Entity Framework's change tracking is pretty good.

    The EntityState property will flag if the entity has been modified. I think it will say "Detached" if you are working with a detached entity/graph. But once you reattach the graph, Entity Framework should do a comparison and flag the object appropriately. Are you certain it updates all columns regardless of what has been changed? I used Entity Framework once before and was having an issue inserting a bunch of data. I used SQL Profiler to find where my problem was. I had accidentally grabbed a bunch of junk data too that was of an incompatible data type. However, I did notice that for every insert and update Entity Framework was making a parameterized function. You may want to use SQL Profiler to check and see what's getting inserted and what's getting updated.

    Also, like I said, reattaching a graph was a near impossiblity in EF v1. EF v4 is tremendously better. Are you using EF v4?

    Thursday, November 04, 2010 4:44 PM
  • well you can use ChangeObjectState to manually change the state of object to modfied. In that case, EF will mark all the properties as wmodified because it doesnot know what has changed from original. Another option is to use self tracking entity on the client side which tracks chanages to hte object when Object Context is not around. When you get on the server you just play those changes using ApplyChanges method. I wrote a blog post on Self Tracking entity if u want to read on that.

    http://weblogs.asp.net/zeeshanhirani/archive/2010/03/30/modifying-self-tracking-entity-on-the-server.aspx

     


    Zeeshan Hirani Entity Framework 4.0 Recipes by Apress
    http://weblogs.asp.net/zeeshanhirani
    Thursday, November 04, 2010 8:33 PM
  • Here's my solution.  In this case, I'm editing a POCO object using MVC and Entity Framework.  I only use some of the fields in the web form, and some of the unused fields are non-nullable in the database.  To avoid retrieving the object (to get the non-nullable fields), I instead iterate through the properties of the MVC form collection and set the properties (not the whole object) as modified in the object context.  When I call save changes, the update works perfectly.

    Controller code:

        [HttpPost]
        public ActionResult Edit(int id, FormCollection model)
        {
          Consultant consultant = new Consultant { Id = id };
    
          if (TryUpdateModel<Consultant>(consultant, model))
          {
            Context.Attach(consultant);
            Context.ChangeObjectState(consultant, model);
            Context.SaveChanges();
    
            return RedirectToAction("Index");
          }
          else
            return View(model);
        }
    
    

    Here's the repository code for ChangeObjectState:

        public void ChangeObjectState(T entity, NameValueCollection model)
        {
          ObjectStateEntry ose = _context.ObjectStateManager.GetObjectStateEntry(entity);
    
          foreach (string key in model.AllKeys)
          {
            // if complex property found, the entire complex property is set as modified
            string property = key.Contains(".") ? key.Remove(key.IndexOf(".")) : key;
    
            ose.SetModifiedProperty(property);
          }
        }
    
    

    Hope this helps

    Sunday, December 26, 2010 11:54 PM
  • Here's my solution.  In this case, I'm editing a POCO object using MVC and Entity Framework.  I only use some of the fields in the web form, and some of the unused fields are non-nullable in the database.  To avoid retrieving the object (to get the non-nullable fields), I instead iterate through the properties of the MVC form collection and set the properties (not the whole object) as modified in the object context.  When I call save changes, the update works perfectly.

    Controller code:

      [HttpPost]
    
      public ActionResult Edit(int id, FormCollection model)
    
      {
    
       Consultant consultant = new Consultant { Id = id };
    
    
    
       if (TryUpdateModel<Consultant>(consultant, model))
    
       {
    
        Context.Attach(consultant);
    
        Context.ChangeObjectState(consultant, model);
    
        Context.SaveChanges();
    
    
    
        return RedirectToAction("Index");
    
       }
    
       else
    
        return View(model);
    
      }
    
    
    
    

    Here's the repository code for ChangeObjectState:

      public void ChangeObjectState(T entity, NameValueCollection model)
    
      {
    
       ObjectStateEntry ose = _context.ObjectStateManager.GetObjectStateEntry(entity);
    
    
    
       foreach (string key in model.AllKeys)
    
       {
    
        // if complex property found, the entire complex property is set as modified
    
        string property = key.Contains(".") ? key.Remove(key.IndexOf(".")) : key;
    
    
    
        ose.SetModifiedProperty(property);
    
       }
    
      }
    
    
    
    

    Hope this helps

    It's not worked for me. In  Context.Attach(consultant) I'm getting the error: "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."

    --------------------------------------------

    краш-тесты авто | калькулятор автокредита



    • Edited by batter111 Tuesday, May 13, 2014 12:08 PM
    Friday, June 10, 2011 6:34 AM