【问题标题】:How to implement ambient transaction in Entity Framework Core?如何在 Entity Framework Core 中实现环境事务?
【发布时间】:2017-08-28 12:28:07
【问题描述】:

我需要在两个模型下实现事务(使用两个分离的有界上下文)。所以代码如下:

  using (TransactionScope scope = new TransactionScope())
  {  
       //Operation 1
        using(var context1 = new Context1())
       {
           context1.Add(someCollection1);
           context1.SaveChanges();
       }
       //Operation 2
       using(var context2 = new Context2())
       {
           context2.Add(someCollection2);
           context2.SaveChanges();
       }

       scope.Complete();
   }

返回异常:

检测到环境事务。实体框架核心 不支持环境事务。看 http://go.microsoft.com/fwlink/?LinkId=800142

在 Link 中,他们建议将一个连接用于两个上下文。并在使用 context1 块时使用 context2。

但如果我为每个模型使用自己的控制器/服务:

using (TransactionScope scope = new TransactionScope())
{  
      service1.DoWork();
      service2.DoWork();

       scope.Complete();
 }

我应该如何实现这个?在方法中添加连接作为参数 - 似乎很荒谬。带连接的初始化服务也是个坏主意。

【问题讨论】:

  • 在两个上下文之间共享一个 DbConnection,然后您可以为两个上下文使用一个事务。 “带连接的初始化服务”不是一个“坏主意”。正是出于这个原因,您的服务应该共享 DbContext 和 DbConnection 实例,而您的依赖注入框架将为您处理这个问题。
  • 如果我在 Service 中添加 dbConnection 作为参数,我会发现我的连接在 Service live 的所有工作时间内都是打开的。我可以花 1 分钟来获取数据和 30 分钟来计算。即使不需要,我也有打开的连接是否正常?
  • 如果您的服务实例是长期存在的(为什么?),那么您需要一些其他对象来表示工作单元。这是假设您有正当理由使用多个 DbContext。
  • 针对一个请求为每个用户提供实时服务。我不能保证该请求会得到及时处理。
  • 使用限界上下文的原因:我们有大约150个表的数据库,不同的流程和客户。

标签: c# entity-framework transactions


【解决方案1】:

您可以像这样使用“上下文”:

using (var context1 = new Context1())
{
    using (var transaction = context1.Database.BeginTransaction())
    {
        try
        {
            context1.Add(someCollection1);
            context1.SaveChanges();

            // if we don't have errors - next step 
            using(var context2 = new Context2())
            {
               // second step
               context2.Add(someCollection2);
               context2.SaveChanges();
            }   

            // if all success - commit first step (second step was success completed)
            transaction.Commit();
        }
        catch (Exception)
        {
            // if we have error - rollback first step (second step not be able accepted)
            transaction.Rollback();
        }
    }
}

如果您将使用许多控制器/服务,则可以将DbConnection 传递到您的服务方法中,并为此操作使用内部方法。你必须封装底层的逻辑。

使用连接的初始化服务也是个坏主意。 - 可能你是对的。但是您可以尝试使用一个连接和单个事务来初始化两种方法。

查看下一个答案,他们可以帮助您:

【讨论】:

  • 如果事务是分布式事务怎么办?它仍然支持吗?是否需要 MSDTC?
【解决方案2】:

使用下面的片段

using (var context = new MyContext())
{
using (var transaction = context.Database.BeginTransaction())
{
    try
    {
        var customer = context.Customers
            .Where(c => c.CustomerId == 2)
            .FirstOrDefault();
            customer.Address = "43 rue St. Laurent";

        context.SaveChanges();
         
        var newCustomer = new Customer
        {
            FirstName = "Elizabeth",
            LastName = "Lincoln",
            Address = "23 Tsawassen Blvd."
        };
         
        context.Customers.Add(newCustomer);
        context.SaveChanges();

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

更多详情:https://entityframeworkcore.com/saving-data-transaction

【讨论】:

    【解决方案3】:

    致你How should I implement this?的声明:

    using (TransactionScope scope = new TransactionScope())
    {  
          service1.DoWork();
          service2.DoWork();
    
          scope.Complete();
     }
    
    

    我正在使用 Controller > Service > Repository 模式,我发现在使用 async 选项时,我也解决了同样的错误。每个存储库都按照通常的方法在Startup.cs 中注入了一个数据库上下文。也许这已在较新版本的 .NET Core 中得到修复,或者我只是在存储库中使用相同的上下文实例?以下对我有用:

    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {  
          service1.DoWork();
          service2.DoWork();
    
          scope.Complete();
     }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-14
      • 1970-01-01
      • 2018-02-05
      • 1970-01-01
      相关资源
      最近更新 更多