EF6中的嵌套事务行为

时间:2015-12-08 15:29:57

标签: c# entity-framework transactions entity-framework-6

我目前正在使用TransactionScope来管理数据层中的事务,但我一直在遇到嵌套事务和异步问题,因此在嵌套事务期间连接似乎已关闭或事务被提升为MSDTC。我没有找到确切的问题,但在阅读之后,看起来这个场景不支持particuarly well,我应该使用Database.BeginTransaction()代替。

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

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

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

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

1 个答案:

答案 0 :(得分:7)

在内部事务可以独立提交或回滚的意义上,没有嵌套事务这样的东西。嵌套事务实际上只保留引用计数。在最后一次提交时,我们得到一个物理提交。在第一次回滚时,我们获得了物理回滚。只是确保你知道这一点。

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

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

  

特别是在我想要使用环境事务而不是创建新事务的场景中。

这对TransactionScope来说非常完美。我认为您切换到BeginTransaction是基于误解。也许你可以在评论中澄清。

  

在嵌套在另一个事务

中时确认Database.BeginTransaction()的行为

在第一段中解释。

  

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

除了缺少db.Connection.Open()调用之外,代码看起来很好(如上所述)。

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