如何在Entity Framework Core中实现环境事务?

6

我需要在两个模型下实现交易(使用两个分离的有界上下文)。所以代码应该像这样:

  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();
   }

返回异常:

检测到环境事务。Entity Framework Core不支持环境事务。请参见http://go.microsoft.com/fwlink/?LinkId=800142

在链接中建议为两个上下文使用一个连接。并在context1的using块中使用context2。

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

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

       scope.Complete();
 }

我该如何实现这个?在方法中添加连接作为参数似乎很荒谬。使用连接初始化服务也不是一个好主意。

在两个上下文之间共享单个DbConnection,然后您可以为两者使用单个事务。 "使用连接初始化服务"不是一个“坏主意”。出于这个原因,您的服务应该共享DbContext和DbConnection实例,并且您的依赖注入框架将为您处理此操作。 - David Browne - Microsoft
如果您的服务实例是长期存在的(为什么?),那么您需要另一个对象来表示工作单元。并且这是在“假设”您有有效的理由使用多个DbContext的情况下。 - David Browne - Microsoft
每个用户只能有一个请求处于活动状态。我不能保证请求会被及时处理。 - error
使用有界上下文的原因:我们拥有大约150个表格的数据库,不同的流程和客户端。 - error
因为这个问题在相当长时间之前就已经在EF Core中得到了解决,所以投票关闭此问题。 - Gert Arnold
显示剩余2条评论
3个回答

2

针对您的问题:我应该如何实现这个?

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

      scope.Complete();
 }

我使用的是控制器 > 服务 > 存储库模式,当我使用异步选项时,发现相同的错误得到了解决。每个存储库都在Startup.cs中注入了一个数据库上下文,遵循通常的方法。也许这在较新版本的.NET Core中已经修复,或者可能是我在多个存储库之间使用了相同的上下文实例?以下内容对我有效:

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

      scope.Complete();
 }


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 这是关于使用Entity Framework Core技术保存数据事务的详细介绍。

显然,这种方法在非关系型数据库(如In-MemoryDatabase)中将被忽略,这可能会导致在实际生产环境中出现不同的行为。 - Rzassar

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传递到服务的方法中进行操作。您必须封装低层逻辑。
使用连接初始化服务也不是一个好主意。也许你是对的。但您可以尝试使用单个连接和单个事务初始化两种方法。
请参阅下面的答案,它们可能会帮助您:

3
如果事务是分布式事务,它是否仍然受支持?它是否需要 MSDTC? - Saeed Neamati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接