与service / repository / unitofwork的抽象事务

时间:2011-06-27 16:57:43

标签: .net database unit-testing unit-of-work

我有大量的事务性数据库访问代码与遗留代码中的业务逻辑混合在一起。 我想将它与服务和存储库分开,事务控制在服务之外,并抽象出事务以实现可测试性。

问题是,我需要在事务中登记IDbConnection参数,用于服务层和存储库层中的每个方法签名,这是我当然不喜欢的:

void Process(Stuff s, IDbConnection connection);

我想找到一种在顶级方法中使用这种方法的方法:

void Process(Stuff stuff, User u)
{
 using(var UoW = ???) // Start transaction
 {
  // various operations in the same transaction
  if( AuthorizationService.AuthorizesUserForStuff(u,stuff) )
  {
   StuffService.Process(stuff);
   AuditService.Success(u, stuff);
  }
  else
   AuditService.Failure(u,stuff);
  UoW.Commit();
 }
}

存储库:

void Process(Stuff s)
{
 IDbCommand command = ??? //aware of current UnitOfWork
 ...
 command.ExecuteNonQuery();
}

遗留代码不使用任何类型的ORM,它是很多存储过程和自定义sql代码,还有一些操作需要往返数据库,所以我不能只用unitofwork注册对象,并执行操作提交方法。

请建议在这种情况下可以使用哪种模式来解决持久性无知问题,并允许控制顶级交易。

2 个答案:

答案 0 :(得分:1)

我在没有实际工作单元的情况下看到您的存储库的唯一方法是通过Static gateway pattern。我理解你的挫败感,但我不认为这样做会好得多。 单元测试也会更难,代码可读性也会更高。

我会非常关注任何其他方法。

答案 1 :(得分:0)

我们使用OR映射,所以我们的解决方案看起来不同。但是,实现相同目标的一种方法可能是使用抽象的持久性上下文来处理具体的数据库内容,将其注入到使用它的所有服务类中,并在“进程”级别从此上下文创建工作单元,然后允许UoW实例通过与上下文实例协作来管理事务状态和连接。

这样的东西
        using (var unitOfWork = Context.CreateUnitOfWork())
        {
            // access injected services and repositories to modify stuff

            unitOfWork.Complete();
        }

在实际执行DB-stuff时,在您的情况下,您将从(注入的)上下文实例获取数据库连接,类似这样的

        using (var conn = Context.AquireDbConnection())
        using (var command = conn.CreateDbCommand())
        {
            // do db stuff

            command.ExecuteNonQuery();
        }

您的具体工作单元/上下文实现应该足够聪明,以便根据是否存在活动的环境业务事务来识别返回的连接是新连接还是环境连接。

显然,您需要针对此机制进行每个请求的隔离,并且每个请求的上下文实例的生命周期都是。