65.9K
CodeProject 正在变化。 阅读更多。
Home

在 N 层体系结构中使用 LINQ to SQL

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (9投票s)

2009年2月27日

CPOL

6分钟阅读

viewsIcon

74039

downloadIcon

2378

如何在N层架构中使用LINQ to SQL。

引言

本文假设您对LINQ to SQL有一些基本的了解和经验。本文针对在N层架构中使用LINQ to SQL的设计方法,解释了某些遇到的问题的解决方案,并提供了一个解决方案的示例代码。

连接对象与断开连接对象

大多数软件应用程序都是数据驱动的。这包括Web / Windows / Web服务 / 网络或其他类型的应用程序。在处理数据访问层时,最常见的设计挑战之一是决定您是要在“连接”模式下还是在“断开连接”模式下工作。

答案实际上取决于您要实现的目标。如果您在2层架构中工作,并且需要高性能,那么最可行的方法是使用“连接”模式。但是,当您在3层架构中工作时,“断开连接”模式将是更合乎逻辑的选择。为什么?在3层架构中,业务对象会从中间层传输到(智能)客户端层。通信是双向的,意味着对象会来回传输。

“连接”意味着对象以某种方式维护与数据层的连接,并且在需要时使用该连接来检索详细信息或子对象。“断开连接”意味着对象会被序列化/反序列化,并且与BOs的连接不会被维护。

image1.jpg

如果您决定在“连接”模式下工作,LINQ to SQL提供了一个非常好的解决方案。生成的ORM使用DataContext来检索信息或子对象。然而,LINQ to SQL适应N层架构并非开箱即用。需要一些额外的工作。本文将解释此方法中遇到的问题以及最佳实践。

将LINQ to SQL与WCF结合使用

.NET 3.0为Web服务带来了一个非常好的增强功能,称为Windows Communication Foundation。它使您能够编写更健壮,更具扩展性的Web服务。

如果您使用普通的Web服务和LINQ to SQL生成的对象,它将不起作用。这是因为生成的类将不包含[ISerializable]属性。但是,它们仍然可以使用DataContractSerializer进行序列化,因为它们具有DataContract属性。这使得WCF成为在不同层之间传输对象的便捷方式。

单向与双向序列化

LINQ to SQL中生成的对象默认不支持序列化。要更改这一点,您必须在LINQ to SQL设计器中将序列化模式更改为“Unidirectional”(单向)。

image2.jpg

但是,单向序列化意味着什么?让我们首先看一下以下数据模型。Category可以拥有多个Material,而Material可以拥有多个Price

image3.jpg

如果使用单向模式,将只生成子关系;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中有一个部分解决方案。

在DataContract序列化中使用(IsReference=true)属性

在.NET Framework 3.5 SP1中,对DataContract进行了一个新的增强,可以使用(IsRference=true)属性进行标记。这将告诉序列化器(DataContractSerializer)该约定是一个引用,因此它会将其作为引用来处理,在这种情况下,它将能够检测到循环依赖。

现在的问题是,没有人更新LINQ to SQL设计器来添加“Bi-Directional”(双向)序列化的新模式。嗯,这是一个真正的问题。我相信他们会在下一版Visual Studio中修复它,但目前,我的英雄damieng在CodePlex上有一个解决方案:http://www.codeplex.com/l2st4

在您的项目中使用的模板

  1. 从上面的链接下载。
  2. 将模板包含到您的项目中
  3. .tt文件重命名为与您的DBML文件相同的名称。
  4. 打开.tt文件,并将SerializeDataContractSP1设置为true。正如我们之前讨论的,这将生成双向序列化的对象。
  5. 右键单击.tt文件并选择“Run Custom Tool”(运行自定义工具)。
  6. 您必须更改原始designer.cs文件的编译选项。将生成操作设置为None(无),而不是Compile(编译),以避免拥有同名生成的类副本。

使用LoadOptions预加载子对象/相关对象

在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();

乐观并发检查

在多用户环境中,很有可能两个用户在相近的时间尝试编辑同一行。假设在3层环境中,发生了以下场景:

  1. 用户A将记录加载到他的智能客户端中。
  2. 用户B也将同一条记录加载到他的智能客户端中。
  3. 用户A进行修改并保存记录。
  4. 用户B进行不同的修改并保存相同的记录。

如果没有并发检查,用户B将覆盖用户A的更改。理想的情况是,用户B将收到一条消息,提示他记录的当前版本已不再有效,并且他在保存修改之前必须加载最新版本。

为此,请在所有可编辑的表中添加一个时间戳列。按照下图所示,从DBML设计器配置属性。生成的SQL查询实际上将包含所有必要的检查。您无需进行任何额外工作。当然,除了捕获行冲突异常并根据您的业务需求进行适当处理之外。

image4.jpg

Using the Code

我希望附带的代码能够很好地说明如何将LINQ用于N层架构。要使用该代码,请首先在您的localhost\SQLEXPRESS实例上创建一个名为“LINQ”的数据库,并使用包含的.sql文件。

请下载AdventureWorks数据库以使代码正常工作:http://www.microsoft.com/downloads/details.aspx?familyid=e719ecf7-9f46-4312-af89-6ad8702e4e6e&displaylang=en

© . All rights reserved.