Attach an entity that is not new, perhaps having been loaded from another DataContext.

This exception using the Linq Attach() method is somewhat perplexing at first:

System.NotSupportedException: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.

This blog post here sort of pointed me in the right direction. But I found the WHY confusing and I found the example confusing.  The following is my implementation of the suggested solution from the previous post.  First, consider the following diagram:

The Contact is the primary object and it has a collection of Addresses.  The State and AddressType tables are simply master tables for reference data. Next we create a public method to handle database modifications:

 public static void SaveContact(Contact contact)
 {
     using (PimDataContext dataContext = CreateDataContext())
     {
         if (contact.ContactID == 0)
         {
             dataContext.Contacts.InsertOnSubmit(contact);
         }
         else
         {
             dataContext.Contacts.Attach(contact, true);
         }
         dataContext.SubmitChanges();
     }
 }

This code works fine for inserts of a Contact with or without any addresses in its child list of addresses.  However, it only works for updates if the contact does not have any child addresses.  If it does have addresses, then it throws the dreaded: "System.NotSupportedException: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported."  When I blindly followed the example from the previously referenced post, I essentially set everything in the Contact object to the default.  For example, this._Addresses = default(EntitySet<Address>);.  But this doesn't do me much good because if I actually made a modification to any addresses, then those changes are now lost as I'm setting the collection of Addresses back to a default empty EntitySet reference.  Additionally, I found myself asking the question, "WHICH entity is it complaining about?" Everything works fine when it's a stand-alone Contact object so it didn't seem feasible for it to be complaining about the Contact object. So I concluded it must be something in the Address object - but what?

A close examination of the generated code for the Address object showed that their were actually 3 EntityRef<> members - Contact, AddressType, and State.  So it seemed it was actually trying to attach my AddressType and State entity (which was never my intent).  Using that information, I found I could make everything work by setting just the EntityRef objects back to the default reference:

public partial class Contact
{
    public void Detach()
    {
        foreach (Address address in this.Addresses)
        {
            address.Detach();
        }
    }
}

public partial class Address
{
    public void Detach()
    {
        this._AddressType = default(EntityRef<AddressType>);
        this._State = default(EntityRef<State>);
    }
}

This allowed my calling code to now just look like this and everything now worked perfectly:

public static void SaveContact(Contact contact)
{
    using (PimDataContext dataContext = CreateDataContext())
    {
        if (contact.ContactID == 0)
        {
            dataContext.Contacts.InsertOnSubmit(contact);
        }
        else
        {
            contact.Detach();

            dataContext.Contacts.Attach(contact, true);
            dataContext.Addresses.AttachAll(contact.Addresses, true);
        }
        dataContext.SubmitChanges();
    }
}

Notice on line 11 I am calling the new Detach() method.  Also notice that I'm line 14, I am now explicitly calling the AttachAll() method for the collection of Addresses.

Looking at the final solution, it all now seems simple. But the troubleshooting that went in to getting there was not simple. While trivial, having the write Detach() methods for all my entity objects is not particularly appealing. Using Linq with stored procedures and explicit function calls would have eliminated much of the mystery as to what was going on behind the scenes trying to figure out why this exception was being thrown (though obviously more work writing stored procedures). These are the types of things that, so far, have me concluding two things: 1) Linq is extremely flexible and powerful, and 2) I still prefer using Linq with my own Stored Procedures.

Update: check out this post here for an example implementation that is even simpler and does not require Detach() methods at all.

Tweet Post Share Update Email RSS