【问题标题】:Is DTO plus UnitOfWork pattern a good approach to design a DAL for a web application?DTO 加 UnitOfWork 模式是为 Web 应用程序设计 DAL 的好方法吗?
【发布时间】:2011-06-01 02:18:54
【问题描述】:

我正在使用实体框架实现 DAL。在我们的应用程序中,我们有三层(DAL、业务层和表示层)。这是一个网络应用程序。当我们开始实现 DAL 时,我们的团队认为 DAL 应该有类,其方法接收业务层上的服务提供的 ObjectContext 并对其进行操作。这个决定背后的基本原理是不同的 ObjectContext 看到不同的 DB 状态,因此某些操作可能会由于外键匹配问题和其他不一致而被拒绝。

我们注意到,从服务层生成和传播对象上下文会在层之间产生高度耦合。因此,我们决定使用 Automapper 映射的 DTO(不是非托管实体或自跟踪实体,它们争论高耦合,将实体暴露给上层且效率低)和 UnitOfWork。所以,这是我的问题:

  1. 这是设计 Web 应用程序 DAL 的正确方法吗?为什么?
  2. 如果您对 1. 的回答是“是”,如何协调 DTO 的概念与 UnitOfWork 模式?
  3. 如果您对 1 的回答为“否”,那么哪种方法可能是为 Web 应用程序设计 DAL 的正确方法?

如果可能,请提供支持您答案的参考书目。

关于目前的设计:

该应用程序计划在三层上开发:表示层、业务层和 DAL。业务层既有门面又有服务

有一个名为 ITransaction 的接口(只有两种方法来处理和保存更改)仅在服务中可见。为了管理事务,有一个类 Transaction 扩展了 ObjectContext 和 ITransaction。我们在设计这一点时考虑到在业务层我们不希望其他 ObjectContext 方法可访问。

在 DAL 上,我们使用两种通用类型(一种用于实体,另一种用于其关联的 DTO)创建了一个抽象存储库。此存储库具有以通​​用方式实现的 CRUD 方法和两个通用方法,用于使用 AutoMapper 映射通用存储库的 DTO 和实体。抽象存储库构造函数将 ITransaction 作为参数,并且它期望 ITransaction 是 ObjectContext,以便将其分配给受保护的 ObjectContext 属性。

具体的存储库应该只接收和返回 .net 类型和 DTO。

我们现在正面临这个问题:创建的通用方法不会为附加实体生成临时或持久 id(直到我们使用SaveChanges(),因此破坏了我们想要的事务性);这意味着服务方法不能使用它来关联 BL 中的 DTO)

【问题讨论】:

    标签: c# design-patterns entity-framework-4 data-access-layer .net-4.0


    【解决方案1】:

    这里发生了很多事情...我假设您使用的是 3 层架构。也就是说,我不清楚你做出的一些设计决定以及做出这些决定的动机是什么。一般来说,我会说你的 ObjectContext 不应该在你的类中传递。应该有某种管理器或存储库类来处理连接管理。这解决了您的数据库状态管理问题。我发现存储库模式在这里非常有效。从那里,您应该能够相当容易地实现工作单元模式,因为您的连接管理将在一个地方处理。鉴于我对您的架构的了解,我会说您应该使用 POCO 策略。使用 POCO 不会将您与任何 ORM 提供者紧密耦合。优点是您的 POCO 将能够与您的 ObjectContext 进行交互(可能通过某种存储库),这将使您能够了解更改跟踪。同样,从那里您将能够实现工作单元(事务)模式,让您完全控制业务事务的行为方式。我发现这是一篇非常有用的文章,可以解释所有这些如何组合在一起。代码有问题,但准确地说明了您所描述的架构类型的最佳实践:Repository, Specification and Unit of Work Implementation

    我对问题 1 的简短回答是“否”。上面的链接提供了我认为对您来说更好的方法。

    【讨论】:

      【解决方案2】:

      您应该看看dependency injection 和控制反转通常意味着什么。这将提供“从外部”控制ObjectContext 生命周期的能力。您可以确保每个 http 请求仅使用 1 个对象上下文实例。为避免手动管理依赖项,我建议使用StructureMap 作为容器。

      另一种有用(但相当棘手且难以正确使用)的技术是持久性抽象。您可以使用所谓的Repository,而不是直接使用ObjectContext,它负责为您的数据存储提供类似API 的集合。这提供了有用的 seam,您可以使用它来切换底层数据存储机制或完全模拟持久性以进行测试。

      正如 Jason 已经建议的那样 - 您还应该使用 POCO`s(普通的旧 clr 对象)。尽管仍然存在与实体框架的隐式耦合,但您应该知道,它比使用生成的类要好得多。

      你可能在别处找不到足够快的东西:

      1. 尽量避免使用unit of work。您的模型应该定义事务边界。
      2. 尽量避免使用generic repositories(也请注意IQueryable)。
      3. 使用repository pattern name 向您的代码发送垃圾邮件不是强制性的。

      另外,您可能会喜欢阅读有关domain driven design 的信息。它有助于处理复杂的业务逻辑,并提供了很好的指导,使代码更少程序化,更面向对象。

      【讨论】:

      • 你有 EF 的 DDD 示例吗?我特别感兴趣的是你如何定义事务边界。
      • @Ladislav 不幸的是,我对 EF 不太熟悉。有点盲目地认为NHibernate仍然优越。但这只会改变存储库部分。事务边界由聚合根本身绘制,因为它们负责有效状态并以原子操作保持不变。正确的建模是关键。
      • 是的,NHibernate 更好。我想看看 DDD + EF 的一些真实世界的例子。我可能会提出一个关于这个主题的问题。
      【解决方案3】:

      我将专注于您当前的问题:老实说,我认为您不应该传递您的 ObjectContext。我认为这会导致问题。我假设控制器或业务服务会将 ObjectContext/ITransaction 传递给存储库。您将如何确保您的 ObjectContext 在下游被正确处理?使用嵌套事务时会发生什么?对于下游事务,由什么管理回滚?

      我认为最好的办法是对您期望如何管理架构中的事务进行更多定义。在您的控制器/服务中使用 TransactionScope 是一个好的开始,因为 ObjectContext 尊重它。当然,您可能需要考虑到控制器/服务可能会调用其他具有事务的控制器/服务。为了允许您希望完全控制您的业务事务和后续数据库调用的场景,您需要创建某种 TransactionManager 类,该类登记并通常管理堆栈上下的事务。我发现NCommon 在抽象和管理事务方面做得非常出色。看看那里的 UnitOfWorkScope 和 TransactionManager 类。尽管我不同意 NCommon 强制存储库依赖 UnitOfWork 的方法,但如果您愿意,可以很容易地重构出来。

      就您的persistantID 问题而言,check this out

      【讨论】:

      • 首先,谢谢!你似乎很感兴趣。我们在服务中创建一个事务,但我们使用 ITransaction;此接口仅公开两个方法:Dispose 和 SaveChances。 Transaction 对象是 ObjectContext 但它被视为 DAL 上的对象;服务只看到 ITransaction。保存和处理事务是程序员对服务方法的责任。我们没有实现嵌套事务,它们很简单,所以我们不考虑回滚。
      【解决方案4】:

      我一直相信代码比程序员的世界更能解释事物。对于这个主题尤其如此。这就是为什么我建议你看看你期望的所有概念都实现的伟大示例应用程序。

      项目名为Sharp Architecture,它以MVCNHibernate 为中心,但您可以使用相同的方法,只需在需要时将NHibernate 部分替换为EF 部分他们。该项目的目的是提供一个应用程序模板,其中包含用于构建 Web 应用程序的所有社区最佳实践。

      它涵盖了使用 ORM、管理事务、管理与 IoC 容器的依赖关系、使用 DTO 等时的所有常见和大多数不常见的主题。

      这是sample application

      我坚持阅读和尝试这个,这对你来说将是一个真正的宝藏,就像对我一样。

      【讨论】:

        猜你喜欢
        • 2023-03-29
        • 1970-01-01
        • 1970-01-01
        • 2010-11-26
        • 1970-01-01
        • 1970-01-01
        • 2011-01-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多