当前位置: 首页 > 知识库问答 >
问题:

工作单元存储库模式:业务事务概念的衰落

邹浩
2023-03-14

工作单元存储库模式组合在一起是当今使用相当广泛的东西。正如Martin Fowler所说,使用UoW的目的是在不了解存储库实际工作方式的情况下形成业务事务(持续无知)。我已经审查了许多实现;并且忽略特定细节(具体/抽象类、接口等),它们或多或少类似于以下内容:

public class RepositoryBase<T>
{
    private UoW _uow;
    public RepositoryBase(UoW uow) // injecting UoW instance via constructor
    {
       _uow = uow;
    }
    public void Add(T entity)
    {
       // Add logic here
    }
    // +other CRUD methods
}

public class UoW
{
    // Holding one repository per domain entity

    public RepositoryBase<Order> OrderRep { get; set; }
    public RepositoryBase<Customer> CustomerRep { get; set; }
    // +other repositories

    public void Commit()
    {
       // Psedudo code: 
       For all the contained repositories do:
           store repository changes.
    }
}

现在我的问题:

UoW公开公共方法Commit来存储更改。此外,因为每个存储库都有UoW的共享实例,所以每个Repostory都可以访问UoW上的方法Commit。由一个存储库调用它会使所有其他存储库也存储它们的更改;因此,整个事务概念崩溃了:

class Repository<T> : RepositoryBase<T>
{
    private UoW _uow;
    public void SomeMethod()
    {
        // some processing or data manipulations here
        _uow.Commit(); // makes other repositories also save their changes
    }
}

我认为这是绝对不允许的。考虑到 UoW(业务事务)的目的,提交方法应仅向启动业务事务(例如业务层)的人公开。令我惊讶的是,我找不到任何解决这个问题的文章。在所有这些中,任何注入的存储库都可以调用 Commit

PS:我知道我可以告诉我的开发人员不要在存储库调用 Commit,但受信任的架构比受信任的开发人员更可靠!

共有3个答案

冯霖
2023-03-14

不要传入UnitOfWork,传入一个具有您需要的方法的接口。如果需要,您仍然可以在原始的具体UnitOfWork实现中实现该接口:

public interface IDbContext
{
   void Add<T>(T entity);
}

public interface IUnitOfWork
{
   void Commit();
}

public class UnitOfWork : IDbContext, IUnitOfWork
{
   public void Add<T>(T entity);
   public void Commit();
}

public class RepositoryBase<T>
{
    private IDbContext _c;

    public RepositoryBase(IDbContext c) 
    {
       _c = c;
    }

    public void Add(T entity)
    {
       _c.Add(entity)
    }
}

编辑

发布这个之后,我重新思考了一下。在UnitOfWork实现中公开Add方法意味着它是两种模式的组合。

我在自己的代码中使用实体框架,这里使用的< code>DbContext被描述为“工作单元和存储库模式的组合”。

我认为最好将两者分开,这意味着我需要两个关于< code>DbContext的包装器,一个用于工作单元位,一个用于存储库位。我在< code>RepositoryBase中进行存储库包装。

关键区别在于,我没有将< code>UnitOfWork传递给存储库,而是传递了< code>DbContext。这意味着< code>BaseRepository可以访问< code>DbContext上的< code>SaveChanges。因为目的是自定义存储库应该继承< code>BaseRepository,所以它们也可以访问< code>DbContext。因此,开发人员可以在使用< code>DbContext的自定义存储库中添加代码。所以我想我的“包装”有点漏...

那么是否值得为 DbContext 创建另一个可以传递给存储库构造函数以关闭它的包装器?不确定是不是...

传递 DbContext 的示例:

实现存储库和工作单元

实体框架中的存储库和工作单元

约翰·帕帕的原始源代码

鱼志诚
2023-03-14

使您的仓库成为您的 UoW 的成员。不要让您的仓库“看到”您的 UoW。让 UoW 处理交易。

秦光启
2023-03-14

我同意你的担心。我更喜欢有一个环境工作单元,其中打开工作单元的最外层函数是决定提交还是中止的函数。被调用的函数可以打开一个工作范围单元,如果有工作范围单元,它会自动在环境UoW中登记,如果没有工作范围单元,则创建一个新的工作范围单元。

我使用的< code>UnitOfWorkScope的实现在很大程度上受到了< code>TransactionScope工作方式的启发。使用环境/范围方法还消除了依赖注入的需要。

执行查询的方法如下所示:

public static Entities.Car GetCar(int id)
{
    using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Reading))
    {
        return uow.DbContext.Cars.Single(c => c.CarId == id);
    }
}

写入的方法如下所示:

using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Writing))
{
    Car c = SharedQueries.GetCar(carId);
    c.Color = "White";
    uow.SaveChanges();
}

请注意,呜。SaveChanges() 调用只会在根(最远)范围的情况下对数据库进行实际保存。否则,将解释为允许根范围保存更改的“好投票”。

UnitOfWorkScope的整个实现可在以下位置获得:http://coding.abel.nu/2012/10/make-the-dbcontext-ambient-with-unitofworkscope/

 类似资料:
  • 问题内容: 关于Spring JPA存储库事务性的1个快速问题。我有未标记为事务性的服务,并调用了Spring JPA存储库方法 它被定义为 问题是它失败,并且“ 没有EntityManager,当前线程没有可用的实际事务- 无法可靠地处理’remove’调用;嵌套异常是javax.persistence.TransactionRequiredException “异常。 好的,我可以通过将服务

  • 关于Spring JPA存储库事务性的1个快速问题。我有一个未标记为事务性的服务,并调用Spring JPA存储库方法 好的,我可以通过标记服务或deleteByEmail(..)来解决它方法作为事务性的,但我就是不明白为什么它现在会崩溃。Spring文档明确指出“存储库实例上的CRUD方法在默认情况下是事务性的。”(http://docs.spring.io/spring-data/jpa/do

  • 本文向大家介绍事务存储器,包括了事务存储器的使用技巧和注意事项,需要的朋友参考一下 事务性内存起源于数据库理论,它为进程同步提供了另一种策略。 内存事务是原子的,是一系列内存读写操作。如果事务中的所有操作都已完成,则将提交内存事务。否则,必须中止操作并回滚。可以通过添加到编程语言中的功能来获得事务存储的便利性。考虑一个例子。假设我们有一个修改共享数据的函数。传统上,此功能将使用互斥锁(或信号量)编

  • 我有一个用例,我需要从一个Kafka主题中消费,做一些工作,生成另一个只有一次语义的Kafka主题,并保存到mongo数据库。看完文档后,我想kafka事务和mongo事务可以同步,但它们仍然是两个不同的事务。在下面的场景中,如果mongo提交失败,是否有方法回滚提交到主题并从消费者处重播的kafka记录。

  • 本文向大家介绍MSSQL事务的存储过程,包括了MSSQL事务的存储过程的使用技巧和注意事项,需要的朋友参考一下 在酒店管理系统开发中,我们会创建房间表和房间类型表(房型表)这两个表,如下图所示: 房型表:RoomType             房间表:Room   首先这两个表的关系:Room是从表,RoomType是主表,两表有主外键关系,RoomType.rTypeId=Room.rType

  • 主要内容:实现,步骤 1,BusinessService.java,步骤 2,EJBService.java,JMSService.java,步骤 3,BusinessLookUp.java,步骤 4,BusinessDelegate.java,步骤 5,Client.java,步骤 6,BusinessDelegatePatternDemo.java,步骤 7业务代表模式(Business Delegate Pattern)用于对表示层和业务层解耦。它基本上是用来减少通信或对表示层代码中的业务