在现代软件开发中,数据驱动的应用非常普遍,无论是Web应用、Windows应用、Web服务还是网络应用,它们都离不开数据访问层。在设计数据访问层时,一个常见的挑战是决定使用“连接”模式还是“断开连接”模式。选择哪种模式取决于应用需求。如果应用是2层架构,并且对性能有较高要求,那么“连接”模式可能是更合适的选择。然而,对于3层架构,选择“断开连接”模式则更为合理。在3层架构中,业务对象(Business Objects)从中间层传输到客户端层,并且这种通信是双向的,意味着对象会在客户端和服务器之间来回传输。
“连接”模式意味着对象与数据层保持连接,当需要检索详细信息或子对象时,会使用这个连接。而“断开连接”模式则意味着对象被序列化/反序列化,并且与业务对象(BOs)不保持连接。LINQ to SQL在“连接”模式下提供了一个很好的解决方案。生成的ORM使用DataContext来检索信息或子对象。然而,将LINQ to SQL适配到N层架构并不是开箱即用的,需要额外的工作。本文将解释这种方法中遇到的问题以及最佳实践。
.NET 3.0引入了Windows Communication Foundation(WCF),这是一个非常强大的Web服务增强功能。它允许编写更加健壮和可扩展的Web服务。如果使用普通的Web服务与LINQ to SQL生成的对象一起工作,这将不起作用,因为生成的类没有[ISerializable]属性。然而,它们仍然可以使用DataContractSerializer进行序列化,因为它们具有DataContract属性。这使得WCF成为在不同层之间传输对象的便捷方式。
LINQ to SQL生成的对象默认不支持序列化。要改变这一点,需要在LINQ to SQL设计器中将序列化模式更改为“单向”。那么,单向序列化是什么意思呢?首先,让看一下以下数据模型。Category可以有多个Material,Material可以有多个Price。如果使用单向模式,只会生成子关系;Material类将有一个属性Prices,如下所示:
[DataMember(Order=20, EmitDefaultValue=false)]
public EntitySet<Price> Prices;
然而,父关系不会被生成,所以不会有指向Category类的关系。这实际上是个问题,因为将错过编写如下代码的机会:
string name = material.Category.Name;
双向序列化的问题是什么?假设使用双向序列化;在这种情况下,Material类将包含对Category类的引用,Category类将包含对Material类的引用。这是一个循环依赖。如果序列化器不够智能以处理这种情况,它将陷入这种循环依赖中。.NET 3.5 SP1中有一个部分解决方案。
在.NET Framework 3.5 SP1中,对DataContract进行了新的增强,可以使用(IsReference=true)属性进行标记。这将告诉序列化器(DataContractSerializer)这个合约是一个引用,因此它将将其作为引用处理,在这种情况下,它将能够检测循环依赖。现在的问题是,没有人更新LINQ to SQL设计器以添加“双向”序列化的新模式。嗯...这确实是个问题。相信他们会在下一版本的Visual Studio中修复它,但目前,偶像damieng在CodePlex上提供了另一个解决方案:
要在项目中使用该模板,请按照以下步骤操作:
在LINQ中,类仅在需要时从数据库加载。然而,在断开连接的环境中,可能需要预加载子对象,并一次性将它们全部发送到客户端。需要节省多次访问服务器的往返时间,并在一次访问中加载所有需要的内容。为此,需要使用数据上下文的加载选项。以下代码是一个示例:
DataClasses1DataContext dc1 = new DataClasses1DataContext();
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Material>(m => m.Category);
dlo.LoadWith<Material>(m => m.Prices);
dc1.LoadOptions = dlo;
return dc1.Materials.ToList();