9. July 2008 04:05
UPDATE: you can read Jowen Mei's post for an updated version of this code.
Anyone who has been trying to use LINQ To SQL with ASP.NET would know of the problems of dealing with state. Since LINQ To SQL does require to maintain state of the entities and the DataContext, the stateless behavior of the web leaves us with a lot of problems that we have to deal with. One of the patterns I have seen over and over again is to keep the entity in memory (through cache, session, viewstate, etc) during postbacks, create a new context, and reattach the entity to the new context. The problem with that is that the entity still holds some information that it was previously created by another datacontext and the moment you want to attach it an exception is thrown. People have been dealing with this problem by detaching the entity and the way you do that is by setting all the Association Properties to default(EntityRef<PropertyType>). So people are extending all their entities and creating a Detach method which detaches each Association Property one by one. So now maintainability is a pain in the you know what. The moment you add a new association you will have to remember to detach it in the Detach method.
After looking at the code created by LINQ to SQL I noticed the following:
private void Initialize()
this._SalesOrderDetails = new EntitySet<SalesOrderDetail>(new Action<SalesOrderDetail>(this.attach_SalesOrderDetails), new Action<SalesOrderDetail>(this.detach_SalesOrderDetails));
this._SalesPerson = default(EntityRef<SalesPerson>);
That's exactly what we need to detach each entity: reinitialize it again. So you can simply fully detach an entity by calling the Initialize method which SqlMetal automatically generates for us. So next time we modify our model and add a new association SqlMetal or Visual Studio will recreate the Initialize method with all the initialization of all the associations for us, which solves the maintainability problem.
A huge warning is that if you are using the partial method OnCreated on your partial class the method will be invoked again.
To go a little further you can extend every entity by creating a partial class and make it inherit from a base class. I called it BaseEntity. This base class can have a Detach method which can invoke the Initialize method of it's inheritor through reflection by doing this:
GetType().GetMethod("Initialize", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(this, null);
Please note that reflection does add overhead, but I think this solution uses the least amount of reflection possible to accomplish something that will otherwise require a lot of maintainability.
I am working on an article on how to use LINQ to SQL in an N-Tier environment with ASP.NET (without WCF). So keep posted as I will notify everyone when the article is published.