实体框架

anti-Patterns 到避免在 N 层应用程序

Daniel Simmons

本文讨论:

  • 了解 n 物理) 层
  • 不要分发您的对象 !
  • 自定义服务或 RESTful 服务?
  • 某些 n 物理) 层反模式
本文涉及以下技术:
实体框架

内容

了解 N 层
anti-Pattern # 1: 紧耦合
anti-Pattern # 2: 假设静态的要求
anti-Pattern # 3: Mishandled 并发操作
anti-Pattern 4: 状态服务
anti-Pattern #5: 两个层装作为三
anti-Pattern 6: Undervaluing 简单

为此实体的成员 Framework 团队,我经常与客户有关生成使用实体框架的应用程序。可能该主题我获取询问超过任何其他操作设计 n 物理) 层应用程序。此文章中, 我将尝试设置的基础您可以生成您的应用程序的这一部分中的成功。相应的大多数被专门用于设计反模式的 n 层通常是我发现的最重要问题。这是一个主题有大量选项和许多问题要考虑,这样很重要在为特定应用程序的决定之前了解整个空间。以后的文章中我将检查 n 物理) 层模式成功和某些关键的 API 和与实体框架,问题并提供传入 Microsoft.NET Framework 4,应使 n 物理) 层得更容易的功能的 sneak 峰值。

了解 N 层

深入讨论,反模式之前,至关重要常见了解 n 物理) 层。

要清除在第一点是在差异层和图层。一个设计良好的应用程序需要通过仔细管理的相关性的多层。这些图层未能居住在一个层或跨多个物理层拆分。一层表示物理分离或允许物理分离必要的至少一个设计时,图层是一个应用程序中, 只是组织的概念。

如果该数据库运行的进程,但除非它涉及到比只是数据库和应用程序的更多层应用程序未调用 n 物理) 层,任何应用程序讨论到数据库的将具有多个层。同样,涉及数据库的每个 ASP.NET 应用程序是从技术上讲 n 物理) 层,因为将数据库、 在 Web 服务器和浏览器。除非您引入 Windows Communication Foundation (WCF) 或 Web 服务,因为大多数情况下在 Web 服务器和浏览器可以被视为单个客户端层不会调用该应用程序 n 层。N 层应用程序是至少具有数据库层,提供了一种服务在中间层和客户端层。

尽管它确实不声音类似的大有问题表面上,证明实现分布于多个层的应用程序很难。有比您料想的更多缺陷。这些缺陷导致 Martin Fowler,企业应用程序体系结构 (Addison-Wesley,2002),可在非常强的语句主题的他簿模式:

不要分发您的对象 !

Martin 调用该分布式对象第一个法则。

与每个设计规则但,有时必须留出设置法律的时间。您的应用程序可能会遇到需要多个层,以便您可以应用更多的计算资源的可伸缩性问题。也许您需要与业务合作伙伴或客户应用程序的 Exchange 数据。它可能只是您具有安全或划分到多台计算机应用程序,或阻止应用程序直接与其他部件的一部分的基础结构约束。时需要多个层,您真正需要它们。

而在本文中介绍的反模式可用于各种应用程序和技术,主焦点将被创建并使用保留数据使用实体框架的自定义 WCF 服务。

毫无疑问,许多 n 物理) 层反模式是失去焦点在应用程序的目标的结果。如果您不保留记住内容目前可以首先,使用 n 物理) 层体系结构或忽略关键的持久性问题,然后很都太容易来获取问题。以下各节将介绍一些常见问题。

自定义的服务或 RESTful 服务?

REST,或 Representational 状态传输,是一种快速获得普及中的 Web 服务。因此这样一您将可能自问一下不同之处是 RESTful 服务和自定义的 Web 服务之间以及为什么可以通过其他选择一种类型。两种类型关键区别是 REST 等服务是资源中心,这是自定义服务时操作的中心。与 REST,您将数据划分为资源提供一个的 URL 的每个资源,实现对这些资源,以便创建、 检索、 更新和删除 (CRUD) 的标准操作。自定义查服务可以实现可以为应用程序的特定的需要定制焦点位于操作,而该的资源和那些操作这意味着任何任意方法。

某些服务很自然适合 REST 模型通常时资源明显并且许多服务包括这些资源进行管理。Exchange 服务器中,对于是实例会使用为组织电子邮件和日历项的一个 REST API。同样,没有照片共享网站在 Internet 上公开 REST API 的。在其他的情况下这些服务不清楚地匹配 REST 等操作,而仍可以适应。发送电子邮件,是例如可以完成通过将资源添加到的发件箱文件夹。这不是您最自然会对发送电子邮件,方法但它不过多的一个扩展。

在其他的情况下但,REST 等操作就不适合和。为了启动驱动器每月的工作流创建资源工资例如检查打印,将为得自然比用于此用途的特定方法。

如果您的服务可以容纳的 REST 限制,这因此将购买您很多优点。ADO.NET 数据服务结合实体框架可以轻松创建 RESTful 服务和客户端使用它们。框架可以提供多个功能 RESTful 服务自动因为服务的限制遵循特定的模式。此外,RESTful 服务有非常广泛的访问,因为它们是这样简单和互操作性。它们正常尤其是当您不知道提前谁客户端可能是。最后,REST 等进行缩放以处理非常大的卷的操作。

对于很多的应用程序的 REST 限制是只是太多。有时域不划分清晰的资源的单个模式或操作同时涉及多个资源。有时用户操作和周围的业务逻辑不会映射很好地 RESTful 操作,更精确地控制需要或比可以容纳这些操作。在这的种情况下自定义服务是该方法转。

您始终可以生成具有多种 REST 和自定义的服务的应用程序。通常,应用程序的理想的解决方案是两者的混合。

anti-Pattern # 1: 紧耦合

很听说的紧耦合 evils。因此总是尽力使您的组件松散耦合尽可能,权限?是的右。

松散耦合比紧密耦合,困难并且通常不为好性能。您最好的意图,从开始,但最终要求是否好处值得成本。可以只创建类的一个实例和直接调用该方法时,为什么引入接口和依赖项的注入?为什么使用映射到的填充 DataTable,并将其周围传而不是数据库的自定义对象生成抽象?

更糟糕的是使问题,您不通常认为,获得认可直到许多更高版本的紧耦合。在短期您获得一些效率,并获得完成,任务,但从长远发展应用程序会变得几乎不可能。

如果您已被构建软件的任何时候在是所有您可能了解耦合折衷方案相当,和谈到层中的模块。当密切配合的模块有时紧密耦合是正确的选择但在其他的情况下组件需要保留彼此的分支的长度,以便可以包含应用程序的更改的涟漪效果。

应用程序的拆分部分涉及到单独的层,耦合的重要性成为得多。为此原因是简单的。层不要始终更改相同的速率。如果您有一个由多个客户端服务,并且不能保证所有客户端将升级根据,然后您更好地确保您可以更改该服务,而不必更改客户端。如果不是,您遇到问题有时称为的剪切的更改的速率。假设应用程序被提取在两个不同的方向,直到它已强制翻录分开。

该技巧就是更改的确定应用程序的部分可能具有不同的比率和哪些部分更改的紧密耦合相互。首先,考虑数据库和在 mid-tier 之间边界。随着您的应用程序的增长没有很可能需要调整数据库以提高性能,并且如果曾经共享多个应用程序之间数据库没有您需要一个应用程序 mid-tier 发展而不更改另一个很好的机会。幸运的是,使用该实体框架已经有助于此处因为其映射系统提供了抽象 mid-tier 代码和数据库之间。相同的问题应考虑在 mid-tier 和客户端之间。

此操作中的反模式一个特别常见和痛苦示例是使用的表适配器从数据库和 Web 服务检索数据,与客户端的 Exchange DataSet 的体系结构。表适配器移到 DataSet 的数据具有相同的架构 (因此紧密离合器数据库以在 mid-tier) 并 Web 服务然后交换该相同的数据集与客户端 (因此紧密离合器在 mid-tier 向客户端)。这种系统是易于创建,有很好地将您引导完成过程的 Visual Studio Tools。但如果这种方式构建系统对系统的任何部分都可能 ripple 到所有其他部件。

anti-Pattern # 2: 假设静态的要求

更改说到系统,有时您设计围绕假设要求将保留静态,但在不断变化的需求具有尤其重要影响的两种情况。一个来自为可信,将客户端,并且其他发生 mid-tier 服务假定客户端将使用特定技术实现时。

虽然不可能的信任边界将意外更改,数据完整性、 安全和信任时,但获取错误的结果是只是太大。如果仅在客户端,对于是实例和 mid-tier 信任您收到的数据是确定将直接发送到数据库而不 revalidating 上执行验证,内容将最终进入错误的机会大于更可能会认为。甚至知道服务仅运行您的 Intranet 内不足以保存您的信息安全的地方。有人可能会创建使用相同的服务的另一个客户端,或修改第一个客户端能够从不同的代码路径跳过验证的调用服务中。谁知道可能会发生。

进一步后一种服务, 它是很可能该正则代码在没有不比预计的方式中使用,如此多通常接受的智慧以便在应当始终验证,,即使这可能意味着验证或执行访问控制多个强制某种程度的安全性,mid-tier。

第二个客户端锁定到特定的技术问题很有问题更可能。技术随时更改。如果应用程序都足够长,内容会发生,强制技术的调整,并客户端是非常容易。您可以最初设计为丰富客户端桌面应用程序应用程序,然后以后发现需要将其移动到移动电话或 Silverlight。如果该案例,设计 Exchange 数据集将服务,然后主要 surgery 需要为服务和所有现有的客户端。

anti-Pattern # 3: Mishandled 并发操作

虽然有一个紧耦合缺点交换数据集并发将是数据集处理和复杂,但的重要区域。遗憾的是许多开发人员不理解管理并发,nuances,将事情更糟糕的并发错误的通常只显示应用程序后在生产中的问题类型。如果幸运的话它将表现为了明显的错误。如果不是,不被检测到都会导致损坏您的数据长期的时间。

在其核心,并发管理是相当简单: 保证数据的完整性,即使如果两个客户端尝试大致同时修改同一数据。特别是 attentive 读者会注意这些问题还提出与 n 层无关的情况下,但并发问题是特别是与该实体框架 n 物理) 层设计的因为的 n 物理) 层方案的实体框架的处理创建唯一的并发性挑战。

大多数的应用程序并发操作选择的管理技术是开放式并发。即使多个客户端可能会同时访问数据库,则完全相同的实体冲突的方式的修改时的次数是非常小的。这样您假设所有内容将出,工作,但采取措施来检测如果出现问题。

检测推动由一个或多个属性,统称为该并发令牌,更改该实体的任何部分更改时。在应用程序读取实体,它将保存并发标记的值。以后,当它希望将该实体写回到该数据库,它首先检查以确保在数据库中的并发标记的值是相同现在它时最初读取该实体。如果有,更新将继续。如果,不更新将停止并引发异常。

实体框架通过查询实体透明地跟踪并发操作令牌与原始值,并检查存在冲突的数据库更新才能支持开放式并发。使用 n 物理) 层应用程序问题是此过程正常透明地仅为长单个 ObjectContext 实例用于跟踪从该查询直到调用 SaveChanges 时该实体。如果您序列化到另一层中的实体,推荐的模式是只足够长对于单个的服务方法调用 mid-tier 保留周围上下文。后续调用将旋转上下文完成每个任务的一个新实例。(创建新的上下文实例,每个服务操作是一个重要建议本身就,顺便。详细信息,请参阅反模式 4: 状态的服务)

一旦开发人员开始若要了解如何实体 Framework API 用于这种断开连接操作,断开连接的实体会断开连接从上下文在的查询发送到另一层,并且然后 re-connected 时间来保存时,没有一个倾向分为一个棘手的模式:

  1. 查询该实体,和其序列化到客户端。到目前为止并发标记的当前值与在原始的值相同的唯一值发送到客户端。
  2. 客户端接收该实体、 更改,和将实体的修改的版本发送回该 mid-tier。
  3. 由于客户端和服务都不明确保持跟踪的并发操作标记,或有哪些属性已被修改,服务查询数据库以获取到一个新创建的上下文的实体的当前状态然后比较数据库中的当前实体和恢复从客户端发送一个之间的值。
  4. 在服务调用后者将执行的 SaveChanges 开放式并发检查同时保留所做的更改。

未看到此问题?实际上有两个问题。

首先,每次更新实体,它必须从数据库中读取两次,一次时它第一次查询,在更新前的第二个时间右在系统上创建大量的额外负载。

第二个,和更是重要在"原始值"实体框架用于检查是否已在数据库中被修改实体来自而不是第一个第二个查询。这就是它来自于发生更新之前的权限的查询。因此结果是开放式并发检查由实体框架将几乎永远不会失败。如果其他人修改第一个查询和第二个之间实体,系统将不检测冲突,因为值用于并发检查是从之前的其他客户端的修改而不是后。

没有仍第二个查询和更新) 之间的小窗口开放式并发检查检测问题,因此您仍然必须编写您的程序来处理该的异常,但您将不真正具有防止数据损坏。

正确的模式是使客户端上的该实体的副本,并修改原始版本和修改后的版本,请发送回或这样不会修改并发操作令牌中写出客户端。如果并发令牌更新的服务器触发器或自动因为它是一个行的版本号 (可能是最好计划仍),则没有理由以客户端上对它进行修改。不变,使用为原始值的存储,可以保留该属性的当前值。

这是一个合理的声音的方法,因为如果客户端中的错误导致将意外修改值,太高会 False 的成功。该错误可能会导致将为 False 的出现故障但的更比 False 成功。

若要将工作,在 mid-tier 接收来自客户端的实体此方法需要将其附加到上下文,以及然后继续通过它的属性手动将其标记为已修改。在任何一的种情况下通过,您将解决这两个反模式问题次。您将无法再查询数据库两次,和并发检查将基于初始查询) 中标记的正确的值而不是某个更高的值。

anti-Pattern 4: 状态服务

开发客户端-服务器解决方案的 comparative 轻松,给定下一个反模式出现当开发人员尝试通过在多个服务调用保持周围上下文简化操作。这看起来很好最初因为它 sidesteps 并发性问题。如果您保留上下文活动在 mid-tier,它将包含正确的原始实体值。当您从客户端回收到实体时, 可以比较与上下文中的该实体的版本更新的实体,并应用适当的更改。如果保存该实体,则进行正确的并发检查,并没有额外的数据库查询是必需。

虽然这种方法似乎表面上轻松,有许多存在处理的问题。管理上下文生存期可以快速棘手。当调用该服务的多个客户端必须维护每个客户端或风险的冲突,它们之间的不同上下文。即使您解决这些问题,您将结束其最主要的可伸缩性问题。

这些可伸缩性问题不是只占用服务器资源的任何客户端的结果。还必须以防止可能出现客户端可能启动的工作,单元但永远不会完成,通过创建的过期方案。进一步,如果您决定,需要您的解决方案出扩展通过引入与多个的 mid-tier 服务器场,则您将不得不维护将与同一个服务器的工作单元的开始位置关联的客户端会话关联。

大量工作和特殊的技术已被扩充在解决这些问题时,实际上,最佳的解决方案是避免这些 altogether 通过保持您 mid-tier 的服务实现无状态。每次进行服务调用,在 mid-tier 应创建必需的资源,处理呼叫,然后释放特定于该调用的所有资源。如果某些信息需要的信息应保留通过客户端而不是在 mid-tier,因此没有会话关联,不需要为过期的工时的未完成的单位和没有服务器中的资源使用特定的客户端之间,进行维护的然后扩展在多个的服务调用的工作单元服务调用。

anti-Pattern #5: 两个层装作为三

相当经常遇到的另一个反模式还是尝试简化该过程。通常它显示为一个请求类似,"为什么不能进行该实体框架序列查询层?"跟几乎可立即,"哦,它时,您可以支持从另一层的启动更新?"

这些可能是 Microsoft 可以将添加到实体框架的功能,但如果您停止并根据的其他问题我讨论了一分钟的考虑它们,您将不得不问题是否这是一个不错的主意。

如果客户端层上,可以创建实体框架 ObjectContext,执行到该上下文中加载实体、 修改这些的实体和则必须向该数据库服务器推从客户端通过该 mid-tier 的更新的 SaveChanges 任何实体框架查询如果可以执行的那么为什么有在 mid-tier 根本?为什么不只是公开数据库直接?

请记住 Fowler 的第一个法则分布式对象。请记住,唯一的情况这种体系结构的意义是您真正,真正需要时。如果您真正需要它,则需要更好的安全性或能力与多个的服务器或某些事,我建议将不真正解决通过引入是只是为数据库的细代理的一个 mid-tier 扩展。您可能会使用此方法 subvert 放入您在公司的策略的限制,但几乎不捕获 n 物理) 层应用程序的时候。我建议是在完全生成 n 物理) 层应用程序满足特定需要,或如果可以获得立即与其,避免 n 物理) 层或者投资。

anti-Pattern 6: Undervaluing 简单

这将我最后 n 物理) 层反模式。避免前面讨论的所有反模式的名称很容易确定您需要创建在 re-validating 最仔细结构式,多层,完全分开,超级可以设置自带的设计。然后您可以花费基础所有您时间构建结构和任何时间实际上将值传递到您的用户。

务必考虑对您的目标,并考虑是否要需要投资 n 物理) 层需要。简单是很好。有时,两层应用程序是只是件事情。有时您需要比的更多层,但所有受您的控制,受信任或有 AJAX,Silverlight 或单击-一次客户端的自动部署,这样做不需担心剪切的更改的速率。

如果您可以使问题更为简单,执行此操作。如果必须,但通过相同的 token 确保置于完整的解决方案的所有工作您都置于满足您的目标的作业不足,无法工作。

Danny Simmons 是在 Microsoft 实体 Framework 团队的开发经理。您可以读取多个实体框架上的他想法和在其他主题blogs.msdn.com/dsimmons/.