实体框架 N 层应用程序模式
在我以前的文章中,我将描述的基础您可以建立成功的 N 层应用程序主要侧重于反模式以避免。 有很多 N 层应用程序的设计决策之前需要考虑的问题。 在本文中,我将介绍 n 层模式,成功的一些关键 API 和特定于问题在 Entity Framework。 此外提供功能中应使 n 层开发得更容易在 Microsoft.NET 框架 4 sneak 出现峰值。
更改设置
更改集模式思想是工作的创建一个可以保留在一起所需单元的数据并,理想情况下,执行客户端上自动跟踪更改的可序列化容器。 容器将一起粘附的一种自定义格式中的工作单位的部分以便这种方法也往往非常全功能和易于使用,mid-tier 和客户端上。
数据集最常见示例这种模式的但其他示例如 EntityBag 示例我编写了一些时间为一个研究此技术使用实体框架的前存在。 这两个示例展示一些在此模式的弊端。 第一次,更改图案位置重要上设置约束客户端因为线格式往往非常特定更改设置和难以进行互操作。 在练习中,客户端必须使用相同的更改集实现该 mid-tier 上使用.NET。 第二个,线格式通常是效率很低。 除了其他的事项更改集被为了处理任意的架构,因此系统开销需要跟踪该实例的架构设计。 与另一个问题更改集实现,如数据集,而不是一定 endemic 在的模式与您可以得到紧密耦合两个或多个层导致问题,如果您有不同的费率更改的易用性。 最后,并在可能最关注的问题是滥用该更改集是多么容易。
某些方式此模式自动执行,submerges 应在眼前的主意在设计解决方案时的关键问题。 精确因为它很容易将数据放到该更改集,发送给该 mid-tier,然后保持,您可以来实现不在 mid-tier 上验证您保持的更改仅在您所期望的类型。 假设您有用于将开支报告添加到结束也修改某人的工资的记帐系统服务。
该更改集模式最适用于在其中可以完全控制客户端部署,以便您可以解决该耦合和技术要求问题的情况下。 也是正确的选择如果要为开发人员效率而不是运行时效率优化。 如果您执行采用这种模式,测试来验证该 mid-tier 上的任何更改规则,请务必而盲目地保持到达任何更改。
DTO
反过来考虑更改从相反末尾集是数据传输的对象,或 DTO。 此模式的目的是在客户端和该 mid-tier 使用分隔不同类型在客户端和它们之间的邮件保存在 mid-tier 上的数据和数据。
DTO 方法需要实现,大多数努力,但正确实现时, 它可以获得最体系结构的优点。 您可以开发和发展您 mid-tier 和客户端上完全独立的计划,因为您可以保留两层以稳定的格式,不管任意一端上所做的更改之间传输的数据。 自然,有时您将需要在两端都将添加一些功能,但通过到映射数据,与传输对象的代码生成版本控制以及向后和向前兼容性,可以管理该功能的。 因为您显式设计它时层之间传输数据的格式,可以使用互操作的方法很好地使用.NET 以外的其他技术的客户端。 如有必要,您可以使用通过网络,发送非常有效的格式,也可以选择,例如,交换仅出于安全原因的实体的数据的子集。
实现 DTO 不利方面是所需的设计的实质上是相同的数据类型的三个不同,该信息类型之间的映射额外工作量。 您可以像使用 DTO 作为该类型在客户端上,要设计的三个 ; 而不是只有两个类型但是,考虑各种不同的快捷方式,使用 LINQ 到对象,以减少必须编写类型 ; 之间移动数据的代码或使用的自动映射为库可以进一步减少通过检测模式,如具有多个类型相同的名称的属性中复制数据的代码。 但有没有办法这种模式涉及更多的工作比任何其他选项的这一事实 — — 至少初始实现。
这是要考虑您的解决方案时成为很大的互操作性、 长期维护,和类似的非常复杂要求该模式。 在长的项目,生命周期就越可能关闭将支付的 DTO。对于很多的项目但是,您可能能够实现具有模式需要较少的努力的目标。
简单的实体
类似于更改设置模式中,简单实体模式重用 mid-tier 实体类型在客户端,但与不同的复杂数据结构层,间通信的换行这些实体的更改集简单的实体努力将最小的数据结构的复杂性并将实体实例直接传递到服务的方法。 简单的实体模式允许只简单属性修改实体实例在客户端上。 如果更复杂的操作所需更改实体之间关系或完成将更新的插入和删除的组合,应将这些操作表示服务方法的结构中。
最简单的实体的优点是方法的所需的任何额外的类型不具有将被放到映射数据从一种类型到另一个。 如果您可以控制客户端的部署,可以重复使用相同的实体结构 (在同一程序集或代理服务器),即使您使用一个客户端技术,而不是.NET,数据结构简单,因此易于进行互操作。 客户端实现是通常是简单的因为是必需的最小的跟踪。 当属性必须修改客户端可以更改它们直接实体实例。 需要多个实体或关系所涉及的操作时特殊服务的方法进行工作。
此模式的主要缺点是更多的方法通常需要该服务上如果您需要完成按多个实体的复杂的方案。 这将导致吵闹的网络通信的客户端具有多个服务调用完成方案或特殊用途服务具有多个参数的方法。
最简单的实体的方法就尤其有效有相对比较简单的客户端或方案是这样的操作是同构的了。 渚嬪的方式 请考虑在其中操作的绝大多数涉及创建新订单的电子商务系统实现。 您可以设计您的应用程序的交互模式,以便在创建新订单的单独操作中执行修改信息,如客户数据。 然后您需要在服务方法通常是只读的数据,而不更改得多的关系,或插入一次新订单的相关实体,一次修改一个实体的查询。 简单的实体模式适用于相当与这种情况。 一个解决方案的整体复杂性向上,您的客户端将成为更是复杂或者当网络性能是关键变化您需要仔细调整您的网络格式,其他模式将更适合。
self-tracking 实体
self-tracking 实体模式专门用来构建简单的实体模式平衡点一个很好很多情况下在中创建单个模式的各种问题。 其目的是创建自己的更改和相关的实体的更改跟踪的智能的实体对象。 若要减少客户端上的约束,这些实体是纯的旧 CLR 对象 (POCO) 不依赖于任何特定的持久性技术 — — 它们只是表示实体和是否在保持不变,新,修改或被标记为删除的一些信息。
因为实体 self-tracking,它们具有一个更改组的易用性的使用特征的很多,但因为跟踪信息中内置实体本身,并且是特定于其架构,线格式可以是比效率更高与更改集。 此外,因为它们是 POCO,它们几个要求对客户端和很好地交互操作。 最后,因为验证逻辑可以内置实体本身,您可以更轻松地保持有关强制实施特定的服务方法的预期的操作按照要求。
有两个主要的缺点 self-tracking 实体与更改集。 第一次,更改一可以允许多个更改集,如果客户端需要调用以检索需要的数据的多个服务方法合并的方式来实现。 类的实现可以完成与 self-tracking 实体时, 很难比更改一。实体定义本身是复杂的第二个,因为它们包含跟踪信息直接而不是保留的信息在单独结构外部实体中时,有些。 通常此信息可以被保存为最小,但是,所以它通常没有多影响可用性的可维护性的实体。
自然,self-tracking 实体不一样经过全面分离为 DTO,还有时效率更高的线路格式可以在创建 DTO 比 self-tracking 实体的时间。 没有任何阻止您从使用 DTO 的混合和 self-tracking 实体,并且,实际上,只要保持跟踪信息的结构尽可能简单,不是难以如果所需的 DTO 中某些以后在发展 self-tracking 实体。
实现使用 N 层在 Entity Framework
具有审阅您的选项,并决定需要 N 层应用程序,您可以选择一种模式和了解哪些缺陷,以避免在客户端技术。 现在您可以获取滚动。 但其中不会在 Entity Framework (EF) 适合于所有这?
在 EF 寻址持久性问题提供基础。 此基础包括 decouples 从数据库结构 ; 您 mid-tier 的数据库与您的概念实体之间一个声明映射只要提供适当的更改跟踪信息 ; 自动并发检查更新并在 mid-tier 透明更改跟踪。 鍙 ﹀ 的方式 在 EF 是一个 LINQ 的提供程序,这意味着它相对容易地创建复杂的查询,可以帮助与实体映射 DTO。
在 EF 可用于在第一个框架的版本中实现任何所述,四种模式,但各种限制 (作为一部分提供的 Visual Studio 2008 SP1/.NET 3.5 SP1) 使模式而不是简单的实体模式非常难以实现。 在 Visual Studio 2010/.NET 4 中在 EF 殑即将鐗堟湰了许多功能已添加了使实现其他模式更容易。 让我们查看未来的版本之前,但,看一下可以采取与该 EF 现在使用简单的实体模式。
并发标记
这里的讨论,最重要点但是,是确保您已指定每个实体的并发标记。 最佳的选择是使用行版本号或等效的一个概念。 在数据库中的行的任何部分更改时将自动更改行的版本。 如果您不能使用行版本下, 一个最佳选项是使用类似于一个时间戳,并将触发器添加到数据库,以便每当修改行时就更新的时间戳。 还可以执行这一操作在客户端,但容易导致细微的数据损坏问题,因为多个客户端可能会无意中采用并发标记相同的新值。 后配置数据库中的相应属性打开实体设计器与您的模型,选择,属性并设置其并发模式属性窗格中为固定而不是默认值的无。 这个选项告诉执行使用该属性的并发检查 EF。 请记住在同一个实体中使用并发模式设置为固定,可以有多个属性,但这通常是不必要。
序列化
您有系统必备组件的下, 一主题后序列化。 您需要一种层之间移动您的实体的方法。 如果您正在使用默认实体由该 EF 生成的代码,并且您正在构建 Windows 通信基础 (WCF) 服务,您的工作做是因为在 EF 自动生成 DataContract 属性类型和持久属性上的您的实体的 DataMember 属性。 这意味着如果到内存中检索相关实体图,整个关系图的序列化自动包括导航的属性。 生成的代码还支持二进制序列化和,超出 XML 序列化,但 XML 序列化仅适用于不以图形的一个实体。
了解另一个重要概念是虽然在默认生成的实体支持序列化,他们的更改跟踪信息存储在 ObjectStateManager (在 ObjectContext 的一个一部分) 不支持序列化。 在简单的实体模式中您通常从该 mid-tier 上数据库中检索未修改的实体并其序列化为该客户端不需要更改的跟踪信息。 该代码可能如下所示:
public Customer GetCustomerByID(string id) { using (var ctx = new NorthwindEntities()) { return ctx.Customers.Where(c => c.CustomerID == id).First(); } }
执行更新的时间时,但是,必须以某种方式,管理更改的跟踪信息,并且这产生了您需要了解该 EF 的下一个重要部分。
使用该 ObjectStateManager
为两层持久性操作,在 ObjectStateManager 会自动为最大一部分的作业。您不必考虑在所有。 跟踪其控制 ; 每个实体的存在性的状态管理器其关键值 ;一个 EntityState 为值可以是不变,修改、 添加,或删除 ;已修改的属性 ; 的列表并且每个原始值修改属性。 时从数据库中检索实体它添加到列表中,状态管理器跟踪的实体,并实体和状态管理器协同工作以保持跟踪信息。 如果实体上设置一个属性,实体的状态自动更改以修改,将属性添加到列表中修改的属性,并保存原始值。 如果您添加或删除实体,跟踪类似的信息。 当该 ObjectContext 上调用 SaveChanges 时,该跟踪信息用于计算更新语句的数据库。 如果成功完成更新,已删除的实体被从上下文和所有其他实体转换到状态不变,以便进程可以重新启动。
将实体发送到另一层时但是,此自动跟踪过程被中断。 若要实现服务的方法上,通过使用从客户端的信息来执行更新的 mid-tier,需要上该 ObjectContext 存在只是此目的的两个特殊的方法:附加和 ApplyPropertyChanges。
该附加方法通知状态管理器启动跟踪实体。 通常,查询自动附加的实体,但如果您检索了某种其他方式 (例如序列化从客户端,) 的实体然后调用附加到开始跟踪。 有两个重要事情附加来记住。
第一次,末尾附加一个成功调用的实体将始终是不变的状态。 如果您希望最终实体到某些其他状态如修改或删除,您需要执行其他步骤以转换该实体的状态。 在起的方式作用附加告诉该 EF,"信任我。 至少在过去一些点这是在数据库中查找此实体的方式"。时将其附加有一个实体的属性值将被视为原始属性的值。 因此如果您检索实体与查询到客户端,将其序列化,然后将其序列化回到该 mid-tier,您可以使用附加它,而不是再次查询。当附加该实体时,并发标记的值将用于并发检查。 (有关危险的再次查询的详细信息,请参阅我在 6 月月刊的 MSDN 杂志 在应用 Anti-Patterns 要避免在 N 层程序anti-pattern Mishandled 并发的说明)。
在第二个要了解有关附加信息是图形的如果您将附加的实体是图形的相关实体的一部分,该附加方法将遍历关系图并将附加的每个找到的实体。 这是因为在 EF 永远不会使关系图中它是在此部分附加和部分不附加一个混合状态。 因此如果在 EF 附加在图表中的一个实体,它需要确保图形的其余部分将变为以及附加。
ApplyPropertyChanges 方法实现断开连接的实体修改方案的另一半。 在另一个实体,ObjectStateManager 外观具有相同键作为其参数和比较两个实体的每个常规属性。 它查找不同的属性时, 状态管理器以匹配作为参数传递给方法实体值中实体上设置该属性值。 效果是相同的像您有执行状态管理器中实体上直接更改时它已被跟踪。 一定要注意则此方法进行操作只能在"常规"属性,因此它会影响只与一个实体,不整个图形的不在导航属性。它专门设计用于简单实体模式,该实体的新副本的位置包含您需要在其属性值中的所有信息没有额外的跟踪信息所需的该函数。
如果放置了附加和 ApplyPropertyChanges 方法一起创建一个简单服务方法更新实体的方法可能类似下面这样:
public void UpdateCustomer(Customer original, Customer modified) { using (var ctx = new NorthwindEntities()) { ctx.Attach(original); ctx.ApplyPropertyChanges(modified.EntityKey.EntitySetName, modified); ctx.SaveChanges(); } }
虽然这些方法使实现服务的简单,这种服务约定现在需要将该实体之前修改客户端添加一些复杂。 次数多此级别就是复杂性的超过要或需要在客户端上。 因此,而不是使用 ApplyPropertyChanges,您可以将修改的实体附加,告诉它的实体应在修改状态和修改每个属性的上该 ObjectStateManager 使用某些较低级别的 API。 这种方法具有减少必须从客户端移动到该 mid-tier (只有一个实体的副本) 的数据的优势,代价是增加数据在数据库中 (每个属性将被更新即使客户端因为没有办法知道哪些属性已被修改,而其未修改只有某些) 某些情况下进行更新。 图 1 显示了这种方法的代码将外观。
图 1 更新服务的方法
public void UpdateCustomer(Customer modified) { using (var ctx = new NorthwindEntities()) { ctx.Attach(modified); var stateEntry = ctx.ObjectStateManager.GetObjectStateEntry(modified); foreach (var propertyName in stateEntry.CurrentValues .DataRecordInfo.FieldMetadata .Select(fm => fm.FieldType.Name)) { stateEntry.SetModifiedProperty(propertyName); } } ctx.SaveChanges(); }
展开服务包括方法用于添加新客户和删除客户也是简单的。 图 2 显示此代码的示例。
图 2 添加和删除服务的方法
public void AddCustomer(Customer customer) { using (var ctx = new NorthwindEntities()) { ctx.AddObject("Customers", customer); ctx.SaveChanges(); } } public void DeleteCustomer(Customer customer) { using (var ctx = new NorthwindEntities()) { ctx.Attach(customer); ctx.DeleteObject(customer); ctx.SaveChanges(); } }
这种方法可以扩展更改实体之间的关系,或执行其他操作的方法。 记住关键概念是您需要第一次进入该状态管理器类似于它已在最初如果您有查询数据库,状态然后效果所需,实体进行的更改,然后调用 SaveChanges。
而不是简单的实体,在.NET 3.5 SP1 中的模式
如果您决定实现其他模式之一使用第一个版本的该 EF,我的第一个建议是阅读下一节解释如何.NET 4 将使操作更容易。 如果您的项目需要在其他模式之一发布.NET 4 之前,但是,以下是要考虑的一些内容。
当然可以实现更改设置模式。 您可以看到这种模式写入到在code.msdn.com/entitybag/ 工作在 EF 的预发布测试版的一个的示例。 此示例不已更新若要使用的该 EF,3.5 SP1 版本,但才能执行此操作是非常轻松。 您可能希望采用即使您选择要生成一个更改集实现从零开始的一个关键步骤是仅在概念模型元数据 (没有映射、 存储模型,或实际连接到数据库需要) 与客户端上创建一个 ObjectContext 并使用作为客户端更改跟踪。
DTO 也是可能的。 实际上,实现 DTO 不是困难得多,EF 的第一个版本比它会在以后发布。 在这两种情况下,您必须编写自己的代码或使用的自动映射您的实体和该 DTO 之间移动数据。 请考虑一个办法是使用 LINQ 预测数据从查询中直接复制到您的 DTO。 渚嬪的方式 如果创建具有仅名称和电话属性的 CustomerDTO 类我可能然后创建一个服务方法来返回一组 CustomerDTOs 如下:
public List<CustomerDTO> GetCustomerDTOs() { using (var ctx = new NorthwindEntities()) { var query = from c in ctx.Customers select new CustomerDTO() { Name = c.ContactName, Phone = c.Phone }; return query.ToList(); } }
遗憾的是,self-tracking 实体将是最难模式在 SP1 版本中实现两个原因。第一次,在 EF.NET 3.5 SP1 中的不支持 POCO,因此您实现的任何 self-tracking 实体有从属关系的.NET,3.5 SP1 版本,序列化格式将不为合适的互操作性。 您可以解决此手动编写代理客户端,但棘手正确实现。 第二个,好 self-tracking 实体的功能之一是您可以创建操作的组合相关的实体的单个图表 — 某些实体可以修改,新建,其他而且其他人仍然标记为删除 — 但处理混合的图表 mid-tier 上实现方法是非常困难。 如果您调用附加方法,它将遍历附加它可以到达的所有内容在整个图表。 同样,如果您调用 AddObject 方法,它将遍历整个图形,并添加它可以到达的所有内容。 这些操作之一后,您会遇到在其中您不能很容易地转换到它们的预期最终状态的某些实体因为状态管理器允许仅特定状态转换的情况。 您可以从修改,不变移动实体实例,但您不能移动中添加不变。 若要附加到上下文的混合的图表,您需要将 shred 到单独的实体的图形添加或附加每个单独,然后重新连接关系。 此代码是非常困难。
.NET 4 API 改进
在即将开始版本中的该 EF 其中将提供与 Visual Studio 2010 和.NET 4,我们做了许多改进轻松实现 n 层模式的痛苦,尤其 self-tracking 实体。 我将按下面的段落中最重要功能的一些。
POCO 的 EF 将支持完整的持久性忽视的实体类。 这意味着您可以创建具有该 EF 或其他相关持久性的 DLL 没有依赖项的实体。 单个实体类用于在 EF 与保留的数据也将在 Silverlight 或早期版本的.NET 起作用。 此外,POCO 有助于隔离业务逻辑,在您从持久性问题的实体,并使具有非常干净的、 可互操作的序列化格式创建类。
改进的 N 层支持 API 使用该 ObjectStateManager 将更容易,因为我们有宽松状态转换约束。 它将可能首先添加或附加整个图形,然后遍历右边的状态更改实体的关系图上。 您将能够设置实体的原始值、 的实体将状态更改为任何的值和更改关系的状态。
外键属性支持 在 EF 的第一个版本支持表示唯一的方法更改关系是通过导航属性或在 RelationshipManager 仅作为完全独立于实体,建模关系。在即将推出的版本您可以构建一个模型中实体公开一个可直接操作的外键属性。
基于 T4 的代码生成要在 EF 的最后一个重要的更改将 T4 模板引擎允许简单的、 完整控制的实体生成的代码的使用。 这是很重要的因为 Microsoft 可以创建和发布模板生成代码的各种方案和使用的模式,这意味着,您可以自定义这些模板,或甚至编写您自己。 我们将发布的模板之一将生成与您所需的没有自定义编码实现 self-tracking 实体模式的类。 所产生的类允许创建非常简单的客户端和服务。
详细了解 我希望本文让您很好的设计问题涉及创建 N 层应用程序和一些特定的提示,用于实现与这些设计调查该 Entity Framework。 没有当然非常了解,因此我鼓励您看一看 从模式的应用程序体系结构指南和 和 Entity Framework 常见问题解答,实践组。
Danny Simmons 是设备管理器在 Entity Framework 在 Microsoft 的团队。 您可以阅读更多的自己的想法上该 Entity Framework 和其他主题,在 blogs.msdn.com/dsimmons。
原地址:http://msdn.microsoft.com/zh-cn/magazine/ee321569.aspx