01 public void Load()
02 {
03 if (this.HasSource)
04 {
05 ItemList<TEntity> entities = this.entities;
06 this.entities = new ItemList<TEntity>();
07 foreach (TEntity local in this.source)
08 {
09 this.entities.Add(local);
10 }
11 ...
12 }
13 }
再进一步就要追溯到 System.Data.Linq.CommonDataServices.GetDeferredSourceFactory(MetaDataMember) 和System.Data.Linq.Mapping.EntitySetValueAccessor。当DataContext对象初始化模型信息时,会调用GetDeferredSourceFactory为指定属性生成相应的DeferredSourceFactory对象,该工厂对象通过 CreateDeferredSource()生成延迟源对象。在执行查询操作时,DataContext将会调用每个对象的EntitySet属性的 SetSource方法,为每一个EntitySet绑定延迟源,由延迟源来调用DataContext实现延迟加载,这样就实现了EntitySet和 DataContext的解耦,让POCO类也变智能了。对于EntitySet,当执行延迟加载后,延迟源将被清空,并且相应的已加载标志也将设为 true。
接下来我们验证一下,为了方便示例我只保留Customer类的Orders作为唯一的Association属性:(文章最后会给出代码下载,有兴趣可以照着验证)
01 Customer customer = null;
02
03 using (var context = CreateNorthwnd())
04 {
05 customer = context.Customers.First();
06 // forces to load order association
07 customer.Orders.Count.Dump();
08 }
09
10 customer.City = "Beijing";
11
12 using (var context = CreateNorthwnd())
13 {
14 context.Customers.Attach(customer);
15 context.SubmitChanges();
16 }
别急,还是错的!虽然customer.Orders.Count的调用让customer.Orders被加载,但Order对象还包含几个未被加载的Association属性,你把Order对象的Association属性定义去掉就对了!
剖析到这里你明白为什么当存在Association或嵌套Association未被赋值或加载,且延迟源不为空时会抛出异常了么?这是因为和需要Attach的对象一样,延迟源关联的DataContext对象已经被销毁了,延迟源无法在加载数据,所以DataContext拒绝关联这样的对象。
说了那么多,是为了让大家能够明白为什么会产生异常,解决的方法很简单,不需要修改实体的定义,同时也是个人认为LINQ2SQL最佳实践之一: