EF6中的嵌套事务行为

Joe*_*ell 12 c# entity-framework transactions entity-framework-6

我目前正在使用TransactionScope来管理我的数据层中的事务,但我一直遇到嵌套事务和异步的问题,在嵌套事务期间连接似乎关闭或者事务被提升为MSDTC.我没有找到确切的问题,但在阅读之后看起来这个场景并没有得到特别好的支持,我应该使用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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

usr*_*usr 7

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

避免使用MSDTC非常重要.无论是with TransactionScope还是with都可以BeginTransaction.使用前者,您需要Open在范围内显式连接,以便EF不会一直打开新连接.

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

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

这非常适合TransactionScope.我认为你的转变BeginTransaction是基于一种误解.也许你可以在评论中澄清一下.

嵌套在另一个事务中时确认Database.BeginTransaction()的行为

在第一段中解释.

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

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

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

  • 我想我真的在问“BeginTransaction”的嵌套调用是否创建一个新事务(相当于“TransactionScopeOption.RequiresNew”)或使用环境事务(相当于“TransactionScopeOption.Required”)?除了我无法让它工作之外,切换到“BeginTransaction”是因为它是他们对 EF6+ 的推荐。我愿意尝试让它与“TransactionScope”一起工作,但是如果我手动打开连接,我会在外部事务完成之前收到异常(事务“已完成但尚未处理”) (2认同)
  • BeginTransaction 就像必需的。同一连接上无法有多个传输。因此,它不可能表现得像 RequiresNew。“作为他们对 EF6 的推荐”我也读过,但我认为没有给出任何理由。奇怪的建议。“我在外部事务完成之前收到异常”我想你应该调查并修复该错误。如果您需要做的只是修复一个小错误,为什么要放弃整个方法呢? (2认同)