起因主要是因为看到博客园又有朋友开始讨论LINQ2SQL的问题,这次说的是Attach。通过解读Attach,可以发现LINQ2SQL内部是如何维护和跟踪对象实例、如何实现延迟加载,并且还可以引发关于延迟加载和N-Tier Application中LINQ2SQL的应用技巧的讨论。本文所讨论内容适用于.Net Framework 3.5版本的LINQ2SQL,所使用数据库是Northwnd。
对于对象添加和删除操作,LINQ2SQL在Table<T>类定义中直接提供了InsertOnSubmit()/DeleteOnSubmit()。而对于对象的更新,由于LINQ2SQL中采取了对象跟踪的机制(可参考LINQ2SQL对象生命周期管理),所以我们在修改了对象属性后无需显式通知DataContext,当调用DataContext.SubmitChanges()时会自动的把我们所做的修改提交到数据库保存。这种基于上下文的操作是非常方便的,否则在代码中会出现大量的Update调用,但是也存在限制——只有在同一个 DataContext对象的作用域内,对象所做的修改才会在SubmitChanges()时得到保存。如:
1 using (var context = new Northwnd())
2 {
3 var customer = context.Customers.First();
4 customer.City = "Beijing";
5 context.SubmitChanges();
6 }
而在Web和N-Tier Application开发时,数据查询和更新同在一个DataContext中往往得不到满足,所以LINQ2SQL在Table<T>类定义了Attach方法,用于把已与查询DataContext上下文断开的对象关联到Table所属的DataContext对象,这样就可以通过新的 DataContext执行对象的更新操作。如:
01 Customer customer = null;
02
03 using (var context1 = new Northwnd())
04 {
05 customer = context1.Customers.First();
06 }
07
08 customer.City = "Beijing";
09
10 using (var context = new Northwnd())
11 {
12 context.Customers.Attach(customer);
13 context.SubmitChanges();
14 }
但是问题来了,这段代码执行错误,抛出以下异常:
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.
这个问题已经不是一个新鲜的问题了,google一下有很多的解决方法,但这看起来很正常的代码为什么会抛出异常呢?其实还是和 DataContext的作用域有关的,本文尝试剖析这个问题,然后还会讨论在N-Tier Application中使用LINQ2SQL的一些须知技巧。
都是Association惹的祸?
出错的地方在System.Data.Linq.Table<T>.Attach(TEntity entity, bool asModified),下列条件只要满足其中一个,就会造成Attach调用失败: