EF6 中的嵌套事务行为

Nested Transaction Behavior in EF6

我目前正在使用 TransactionScope 来管理我的数据层中的事务,但我一直 运行 遇到嵌套事务和异步问题,据此连接似乎在嵌套事务期间关闭或事务是晋升为 MSDTC。我还没有找到确切的问题,但在仔细阅读之后,似乎不支持 particuarly well 这种情况,我应该改用 Database.BeginTransaction()。

我的问题是我找不到有关 Database.BeginTransaction() 如何处理嵌套事务的信息,特别是在我想要使用环境事务而不是创建新事务的情况下。我怀疑它不打算以这种方式工作,如果我想管理嵌套事务,我应该抽象出事务管理以给我更多的控制权。

不想添加不必要的抽象层我想知道是否有人在这方面有经验并且可以确认嵌套在另一个事务中的 Database.BeginTransaction() 的行为?

关于我的 DAL 的其他信息:基于 CQS 模式,我倾向于将 Db 相关代码封装在命令或查询处理程序中,因此 simplified/contrived 这种嵌套如何发生的示例是:

public class AddBlogPostHandler
{
    private readonly MyDbContext _myDbContext;

    public AddBlogPostHandler(MyDbContext myDbContext)
    {
        _myDbContext = myDbContext;
    }

    public async Task ExecuteAsync(AddBlogPostCommand command)
    {
        using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            // .. code to create and add a draft blog post to the context
            await _myDbContext.SaveChangesAsync();

            var publishBlogPostCommand = new PublishBlogPostCommand();
            // ..set some variables on the PublishBlogPostCommand
            await PublishBlogPostAsync(command);

            scope.Complete();
        }
    }
}

public class PublishBlogPostHandler
{
    private readonly MyDbContext _myDbContext;

    public PublishBlogPostHandler(MyDbContext myDbContext)
    {
        _myDbContext = myDbContext;
    }

    public async Task ExecuteAsync(PublishBlogPostCommand command)
    {
        using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            // .. some code to do one set of update
            await _myDbContext.SaveChangesAsync();

            // .. some other db updates that need to be run separately
            await _myDbContext.SaveChangesAsync();

            scope.Complete();
        }
    }
}

在内部事务可以独立提交或回滚的意义上,不存在嵌套事务。嵌套事务实际上只维护一个引用计数。在最后一次提交时,我们得到了物理提交。在第一次回滚时,我们得到一个物理回滚。只是确保您知道这一点。

避免使用 MSDTC 很重要。 TransactionScopeBeginTransaction 都可以做到这一点。对于前者,您需要明确 Open 范围内的连接,以便 EF 不会一直打开新连接。

正如您在问题中所读到的,这是 EF 中的一个缺陷(L2S 没有)。请花时间对问题发表评论,以确保团队知道客户 运行 遇到了这个问题。

particularly in my scenario where i'm wanting to use the ambient transaction rather than create a new one.

这非常适合 TransactionScope。我认为您改用 BeginTransaction 是基于误解。也许你可以在评论中澄清。

confirm the behavior of Database.BeginTransaction() when nested inside another transaction

在第一段中解释。

Additional information about my DAL: Based on CQS pattern, I tend to encapsulate Db related code in command or query handlers, so a simplified/contrived example of how this nesting occurs would be:

代码看起来不错,除了缺少 db.Connection.Open() 调用(如上所述)。

此模式将支持在同一事务中执行多个查询和命令。只需将另一个范围包裹在它周围。确保不要打开连接两次,例如在采取行动之前检查 conn.State