It is clear from the forums that this whole business of attaching detached objects in LINQ to SQL (DLinq) is confusing. Some of it is intrinsic, some is perhaps our design and perhaps a bit attributable to the misnomer “detached object support”. More about the misnomer later but first here is the skinny on Attach() set of APIs.
Think of a DataContext instance as a happy universe of objects where the entities (or objects with unique keys) are known to the DataContext instance since they were retrieved using that instance and the unknown ones are simply new entities that need to be inserted. Unfortunately, this simple picture is complicated by the fact that in a multi-tier system, entities may be sent over the wire in an ASP.NET app or web service client and then they may need to be brought back to the mid-tier for update. Add to that the fact that the mid-tier is often stateless so the DataContext instance used to retrieve the entity from the database may be long gone. A new DataContext instance can be spun up for update but the new one does not know about the deserialized entity that it did not retrieve. Attach() solves this problem by telling the new DataContext instance that the attached entity is meant to be updated and should not be inserted into the database.
Attach() needs to preserve the optimistic concurrency capability since that is how concurrent change conflicts can be detected and handled. So Attach() needs to deal with current and original values. This is what gives rise to the different flavors.
- Original values used for conflict detection: db.Customers.Attach(originalCust) should be used to attach the original values. The instance can then be modified (playback) before calling SubmitChanges().
- Original and current copies available: db.Customers.Attach(currentCust, originalCust) does it in one shot. Of course, this requires two instances with original and current values respectively.
- Timestamp or no optimistic concurrency members: db.Products.Attach(currentProd) In this case, original values are not required so current entity instance is enough. There is no playback needed before calling SubmitChanges().
When it comes to “detached object support”, a question I often get is – what do I call to detach an entity? The answer is nothing! If an entity is serialized and deserialized back, it is already detached from the DataContext instance used for retrieval. If not, it may have deferred loaders that are tethered to the original DataContext. More significantly, Attach() is not intended to enable movement of entities across DataContext instances in the same app domain. This is not the intent (more about why that’s the case some other day). In fact as I have mentioned on the LINQ forum, this is likely to cause an exception sooner or later so we have modified Attach() to throw if it is used on an object that is still attached to some DataContext instance. This change was done after beta2 and should be visible in RTM.