Steve Michelotti

C#, ASP.NET, and other stuff

  Home  |   Contact  |   Syndication    |   Login
  51 Posts | 1 Stories | 155 Comments | 52 Trackbacks

News



Tag Cloud


Archives

Post Categories

Image Galleries

Articles

Blogs

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:

   1:  public static void SaveContact(Contact contact)
   2:  {
   3:      using (PimDataContext dataContext = CreateDataContext())
   4:      {
   5:          if (contact.ContactID == 0)
   6:          {
   7:              dataContext.Contacts.InsertOnSubmit(contact);
   8:          }
   9:          else
  10:          {            
  11:              dataContext.Contacts.Attach(contact, true);
  12:          }
  13:          dataContext.SubmitChanges();
  14:      }
  15:  }

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:

   1:  public partial class Contact
   2:  {
   3:      public void Detach()
   4:      {
   5:          foreach (Address address in this.Addresses)
   6:          {
   7:              address.Detach();
   8:          }
   9:      }
  10:  }
  11:   
  12:  public partial class Address
  13:  {
  14:      public void Detach()
  15:      {
  16:          this._AddressType = default(EntityRef<AddressType>);
  17:          this._State = default(EntityRef<State>);
  18:      }
  19:  }

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

   1:  public static void SaveContact(Contact contact)
   2:  {
   3:      using (PimDataContext dataContext = CreateDataContext())
   4:      {
   5:          if (contact.ContactID == 0)
   6:          {
   7:              dataContext.Contacts.InsertOnSubmit(contact);
   8:          }
   9:          else
  10:          {
  11:              contact.Detach();
  12:              
  13:              dataContext.Contacts.Attach(contact, true);
  14:              dataContext.Addresses.AttachAll(contact.Addresses, true);
  15:          }
  16:          dataContext.SubmitChanges();
  17:      }
  18:  }

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 simplier and does not require Detach() methods at all.

posted on Tuesday, December 25, 2007 2:20 PM

Feedback

# re: Linq - Attach an entity that is not new, perhaps having been loaded from another DataContext. 12/26/2007 9:05 PM Christian
Hi!
And what if you changes the state (this is a change to a address, too!) ? If you save the customer objekt this change will get lost, right?

# re: Linq - Attach an entity that is not new, perhaps having been loaded from another DataContext. 12/26/2007 9:59 PM Steve
The above code works fine whether the change you are making is to the Customer object, one of its Address objects or both.

# re: Linq - Attach an entity that is not new, perhaps having been loaded from another DataContext. 1/10/2008 12:02 PM Fabricio
I'm facing the very same issue, but the code above has not solved my problem... I don't know why, but I'm still getting the same error when I try to attach an object got from another context.

# re: Linq - Attach an entity that is not new, perhaps having been loaded from another DataContext. 1/25/2008 12:44 PM Rob
Is this how it's actually meant to work? Dtaching everything seems so cumbersome and not particularly MS. Is it still the best way? I can't find any other references to this technique specifically for child entities. Has you found a better solution other than to remove relationships? :-)

Rob

# re: Linq - Attach an entity that is not new, perhaps having been loaded from another DataContext. 1/25/2008 2:43 PM Steve
Rob - check out my post here for a solution that does not require Detach(): http://geekswithblogs.net/michelotti/archive/2007/12/27/118022.aspx.

Additionally, you can click on the association "line" in the data context diagram and delete it.

# re: Linq - Attach an entity that is not new, perhaps having been loaded from another DataContext. 1/26/2008 2:02 AM Rob
Hi Steve - thanks, I've read over that - but that then removes the relationship so that I can't access the child entity which I need. I could create another DBML file which has no relationships and use that datacontext, but that removes the domain model that I'm trying to build.

# re: Linq - Attach an entity that is not new, perhaps having been loaded from another DataContext. 1/27/2008 1:50 PM Steve
Rob - It only removed the relationship between the reference data - the relationship between the Contact and the child Addresses still exists. Additionally, check out this post: http://geekswithblogs.net/michelotti/archive/2007/12/30/118076.aspx.

Hard for me to see what's going on without looking at the code. Feel free to email it to me if you want.

Post Feedback

Title:
Name:
Email: (never displayed)
Url:
Comments: 
Please add 8 and 2 and type the answer here: