一个 DbContext Instance 跨越多个 Repositories

One DbContext Instance spans multiple Repositories

这些是我在 Web Api 设置中的 AutoFac-DI 定义:

 builder.RegisterType<MyContext>().As<MyContext>().InstancePerRequest();
 builder.RegisterType<TestRepository>().InstancePerRequest();
 builder.RegisterType<SchoolclassCodeRepository>().InstancePerRequest();
 builder.RegisterType<TestService>().InstancePerRequest();

TestService 构造函数接受 TestRepository 和 SchoolclassCodeRepository。两个存储库都接受相同的 MyContext 实例。

我同意Is it wise to use same DbContext with multiple repositories?

尽管如此,共享上下文还有其他很好的理由,其中之一(恕我直言)是上下文必须跟踪实体的状态,如果你得到一个实体,处置context,对实体进行一些修改,然后附加到一个新的上下文,这个新的上下文必须访问数据库,这样它才能弄清楚实体的状态。同样,如果您正在处理实体图(发票及其所有 InvoiceItems),那么新上下文将必须获取图中的所有实体以确定它们的状态。

但现在我用这种架构撞到了单行道!

如果我必须进行跨越多个存储库的事务怎么办?

使用 EF6,您可以在没有存储库的情况下这样做:

using(NorthwindEntities db = new NorthwindEntities())
{
    DbContextTransaction transaction = db.Database.BeginTransaction();

    try
    {
        //insert record 1
        Customer obj1 = new Customer();
        obj1.CustomerID = "ABCDE";
        db.Customers.Add(obj1);
        db.SaveChanges();

        //insert record 2
        Customer obj2 = new Customer();
        obj2.CustomerID = "PQRST";
        db.Customers.Add(obj2);   
        db.SaveChanges();

        transaction.Commit();
    }
    catch
    {
        transaction.Rollback();
    }
}

当我现在使用上面的示例并尝试对服务中的 2 个存储库执行相同的操作时,我遇到了一个严重的问题。

如何在不更改我的存储库的情况下在多个存储库上创建事务?

我想要的样例:

在我的 TestService 中我想做的大致是:

public void Save()
{
  // Open Transaction
  // testRepo.Insert();
  // schoolclassCodeRepo.Delete();
  // Commit Transaction
}

更新

在我的 TestService 中,我将存储库中的所有实体映射到 DTO 对象,然后在我的网络 api 控制器中通过数据 + 链接(Rest)丰富这些对象。

更新 2

将所有 Repository 方法实现为 DbContext 的扩展方法不是更好吗,这样我可以直接在注入到我的 TestService 中的一个 DbContext 上调用我的服务中的 "Repo extension methods"?

更新 3 解决方案代码

public async Task<bool> DeleteSchoolyearAsync(int id)
{
    using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        try
        {
            await testRepository.DeleteTestTypes(id);
            await schoolyearRepository.DeleteAsync(id);
            scope.Complete(); // Rollback is done due to using statement...
            return true;

        }
        catch (System.Exception)
        {
            return false;
        }
    }
}

此代码运行良好!

您可以使用事务范围而不是 dbcontexttransaction 来执行此操作:

using (var scope = new TransactionScope(TransactionScopeOption.Required,
                                            new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
    {

  // Your code

 scope.Complete();

}

请注意,当您跨数据库使用它时,它将使用 MSDTC。

您没有更改存储库,但您的体系结构中肯定缺少 工作单元。这是您为多个存储库共享单个上下文的地方。

将 UoW 视为 DbContext,其中存储库是 DbSet。

UoW uow = new UoW( context );

uow.BeginTransaction();

uow.Repository1.... // query, insert, update, delete
uow.Repository2....

uow.Commit();

一个典型的实现只是公开多个存储库:

public class UoW {

   public UoW( DbContext ctx ) {

      this._ctx = ctx;

   }

   private Repository1 _repo1;

   public Repository1 Repo1
   {
      get
      {
          if ( _repo1 == null )
              _repo1 = new Repository1( this._ctx );
          return _repo1;
      }

      ...

如果您需要有关这方面的完整教程,请查看此处:

http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application