一个DbContext实例跨越多个存储库

时间:2015-06-22 18:13:59

标签: c# entity-framework repository-pattern

这些是我的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?

分享上下文有其他充分的理由,其中一个(恕我直言)是上下文必须跟踪实体的状态,如果你得到一个实体,处理上下文,做一些修改实体,然后附加到新的上下文,这个新的上下文必须到达数据库,以便它可以找出实体的状态。同样,如果您正在处理实体图表(发票及其所有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个存储库执行相同操作时,我面临严重问题。

  • 我的服务中没有可用的DbContext。
  • DbContext是一个DataProvider / Layer关注点,应该保留在存储库中。

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

示例我想要的东西:

在我的TestService中,我想粗略地做一下:

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

更新

在我的TestService中,我将所有实体从repos映射到DTO对象,然后在我的web api控制器中通过数据+链接(Rest)进行丰富。

更新2

  • Repository模式使数据访问方法可重用,这很好。
  • 但它使多个存储库上的事务共享相同的DbContext是不可能的。

实现所有Repository方法作为DbContext的扩展方法并不是更好,这样我可以直接在注入TestService的ONE DbContext中调用我的服务中的“Repo扩展方法”吗?

更新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;
        }
    }
}

此代码工作正常!

2 个答案:

答案 0 :(得分:3)

您不会更改您的存储库,但您的架构中肯定缺少工作单元。这是您共享多个存储库的单个上下文的位置。

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

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

答案 1 :(得分:1)

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

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

  // Your code

 scope.Complete();

}

注意在跨数据库使用它时,它将使用MSDTC。