【问题标题】:Why would I want to use UnitOfWork with Repository Pattern?为什么我想将 UnitOfWork 与 Repository Pattern 一起使用?
【发布时间】:2012-01-31 23:43:30
【问题描述】:

我在网上看到了很多关于 UnitOfWork 和 Repo Pattern 的内容,但仍然不清楚为什么以及何时使用——这让我有些困惑。

考虑到我可以按照本文What are best practices for managing DataContext 中的建议,通过使用 IoC 来使用 DI 来测试我的存储库。我正在考虑将上下文作为对我的存储库构造函数的依赖项传递,然后像这样处理它?:

public interface ICustomObjectContext : IDisposable {}
public IRepository<T> // Not sure if I need to reference IDisposable here
public IMyRepository : IRepository<MyRepository> {}

public class MyRepository : IMyRepository
{
    private readonly ICustomObjectContext _customObjectContext;

    public MyRepository(ICustomObjectContext customObjectContext)
    {
        _customObjectContext = customObjectContext;
    }

    public void Dispose()
    {
        if (_customObjectContext != null)
        {
            _customObjectContext.Dispose();
        }
    }

    ...

}

我目前对将 UnitOfWork 与 Repository Pattern 结合使用的理解是跨多个存储库执行操作——这种行为似乎与 @Ladislav Mrnka 对 Web 应用程序的建议相矛盾:

对于 Web 应用程序,每个请求使用单个上下文。对于 Web 服务,每次调用使用单个上下文。在 WinForms 或 WPF 应用程序中,每个表单或每个演示者使用单个上下文。可能有一些特殊要求不允许使用这种方法,但在大多数情况下这就足够了。

查看完整答案here

如果我对他的理解正确,则 DataContext 应该是短暂的,并在每个请求或演示者的基础上使用(在其他帖子中也看到了这一点)。在这种情况下,repo 对上下文执行操作是合适的,因为范围仅限于使用它的组件——对吗?

我的存储库在 IoC 中注册为临时存储库,因此我应该为每个请求获取一个新存储库。如果这是正确的,那么我应该为每个请求获得一个新的上下文(上面的代码),然后处理它——也就是说......是否遵循上述约定?

【问题讨论】:

  • 与您的问题没有太大关系:但是在您作为依赖注入到此类的类中放置一个对象看起来很邪恶。该类显然不负责实例化对象,因此它不负责处置它。
  • 感谢@Slauma 在下面 Wouter 的说明中指出。我需要解决这个问题。

标签: asp.net entity-framework-4.1 ioc-container repository-pattern unit-of-work


【解决方案1】:

ObjectContext/DbContext 是 UnitOfWork 模式的实现。它封装了几个操作,并确保它们在一个事务中提交到数据库。

您唯一要做的就是将其包装在您自己的类中,以确保您不依赖于其余代码中的特定实现。

在您的情况下,问题在于您的Context 不应该被您的Repository 处理掉。 Repository 不是实例化Context 的那个,所以它也不应该丢弃它。封装多个存储库的UnitOfWork 负责创建和处置Context,您将在UnitOfWork 上调用Save 方法。

代码可能如下所示:

using (IUnitOfWork unitOfWork = new UnitOfWork())
{
   PersonRepository personRepository = new PersonRepository(unitOfWork);
   var person = personRepository.FindById(personId);

   ProductRepository productRepository = new ProductRepository(unitOfWork);
   var product= productRepository.FindById(productId);

   p.CreateOrder(orderId, product);
   personRepository.Save();
}

【讨论】:

  • 所以@Wouter 如果我将我的上下文注册为瞬态(使用城堡),那么我不必担心正确处理上下文?
  • @Rich 抱歉,我不使用 Castle,但我发布了一个代码示例,我如何使用 UoW 模式。 using 语句确保 UoW 处理上下文(尽管我们不执行 new UnitOfWork() 而是使用 DI)
  • 以防其他人遇到此答案和 cmets。根据我在 SO 上看到的建议,应该使用 PerWebRequest Lifestyle 注册上下文。
【解决方案2】:

如果我想在存储库跨一个 Web 请求共享相同的上下文实例时将上下文的 SaveChanges 推离存储库,我会使用简化的 UoW。

我想您的存储库中有类似 Save() 方法的东西,看起来类似于 _customObjectContext.SaveChanges()。现在让我们假设您有两种方法,其中包含业务逻辑并使用 repos 来持久化 DB 中的更改。为了简单起见,我们将它们称为MethodAMethodB,它们都包含相当多的用于执行某些活动的逻辑。 MethodA 在系统中单独使用,但由于某种原因也被MethodB 调用。发生的情况是MethodA 将更改保存在某个存储库中,并且由于我们仍然在MethodB 中所做的相同请求更改,在它调用MethodA 之前,无论我们是否想要它也将被保存。所以在这种情况下,我们无意中破坏了MethodB 内部的事务,使代码更难理解。

我希望我描述得足够清楚,这并不容易。无论如何,除此之外,我看不出为什么 UoW 会对您的情况有所帮助。正如 Dennis Traub 非常正确地指出的那样,ObjectContext 和 DbContext 实际上是 UoW 的实现,因此您可能会在自己实现时重新发明轮子。

【讨论】:

  • 丹尼斯的回答提供了最丰富的信息。 +1 为您的回答 - 它有帮助。谢谢@dmusial。
【解决方案3】:

据我了解,工作单元模式不一定涵盖多个上下文。它只是封装了一个操作或——嗯——工作单元,类似于事务。

创建上下文基本上会启动一个工作单元;调用DbContext.SaveChanges() 完成。

我什至可以说,在其当前实现中,Entity Framework 的 DbContext / ObjectContext 既类似于存储库模式,也类似于工作单元模式。

【讨论】:

  • 谢谢@Dennis。 DbContext/ObjectContext 类似于 repo 和 UoW 模式。 我在研究 UoW/Repos & Entity 时看到了这一点。所以在您看来,UoW 与 Entity 是没有必要的?
  • @Rich 我想说这种模式是必要的。这是因为在域驱动设计中,您只为每个聚合根使用一个存储库,而不是为数据库中的每个表(直接使用上下文时会发生这种情况)。在使用存储库时,您可以创建更具体的方法,例如 FindOrdersForCustomer 并在一个地方实现它们。使用 UoW 和存储库模式可确保您使用 DRY(不要重复自己)并将所有数据访问逻辑保存在一个位置。
  • 一如既往,这取决于您的实际要求。但我更愿意使用 EF 中的内置 UoW,只要我不需要跨越上下文边界(正如你已经提到的那样,这可能表明存在设计缺陷)。
  • @WouterdeKort 实体框架确实已经实现了 UoW 和存储库(后者由 Martin Fowler 定义,而不是 Eric Evan 的定义),因此在顶部再次实现它们可以封装数据访问,但也会导致复杂性增加.
  • @DennisTraub 您真的想传递您的 ObjectContext 并让您团队中的所有开发人员针对它编写 linq 查询吗?或者您想将所有数据库操作封装在特定层中并在一个地方进行优化?我已经完成了一个没有 Uow/Repository 层的项目,当我们遇到一些性能问题时,我们不得不到处更改查询。顺便说一句,如果您不将实体框架用作 DDD 中的域模型 (martinfowler.com/bliki/AnemicDomainModel.html),为什么还要使用实体框架来建模域模型?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-14
  • 1970-01-01
  • 2023-04-06
  • 2018-05-04
相关资源
最近更新 更多