`DbContext.Database.BeginTransaction`不能嵌套?

时间:2016-06-28 10:25:56

标签: entity-framework entity-framework-6

我尝试使用EF6方式使用事务并注意到以下代码

using (db.Database.BeginTransaction())
{
    // something
    using (db.Database.BeginTransaction())
    {
        /// something
    }
}

在第二个开始事务中抛出异常,告诉我

The connection is already in a transaction and cannot participate in another
transaction. EntityClient does not support parallel transactions.

不是我想要做一个并行事务,无论在一个连接上可能意味着什么。我只想要一个嵌套的。

这确实不支持正在进行的事情吗?

3 个答案:

答案 0 :(得分:1)

限制:我假设,当内部事务失败时,外部事务也应该失败。所以你不想回滚并在内部做其他事情。

我刚遇到问题,当覆盖 SaveChanges 时,应用一些数据库更改跟踪器,但不知道,如果我们已经有一个事务。所以这定义了用例。

我创建了一个自定义事务,如果我们还没有一个事务,它只会启动一个事务。任何提交/回滚/处置仅在我们最初没有事务时应用:

public class FallbackTransaction : IDisposable
{
    private readonly System.Data.Entity.Database _database;

    private DbContextTransaction _fallbackTransaction;

    public FallbackTransaction(System.Data.Entity.Database database)
    {
        _database = database;
    }

    public void Dispose()
    {
        _fallbackTransaction?.Dispose();
    }

    public void Begin()
    {
        if (_database.CurrentTransaction == null)
        {
            _fallbackTransaction = _database.BeginTransaction();
        }
    }

    public void Commit()
    {
        _fallbackTransaction?.Commit();
    }

    public void Rollback()
    {
        _fallbackTransaction?.Rollback();
    }
}

然后我创建了一个扩展方法:

public static FallbackTransaction BeginFallbackTransaction(this System.Data.Entity.Database database)
{
    var transaction = new FallbackTransaction(database);
    transaction.Begin();
    return transaction;
}

所以我们可以这样做:

using (var transaction = dbContext.Database.BeginFallbackTransaction())
{
    // do something
    transaction.Commit();
}

这是基于 the answerjbl 的想法

答案 1 :(得分:0)

您可以使用CurrentTransaction属性。

using (db.Database.BeginTransaction())
{
    if(db.Database.CurrentTransaction!=null)
    {
     /// something    
    }
    else
    {
        using (db.Database.BeginTransaction())
        {
         /// the same something
        }
    }
}

语法相当尴尬。我经常依赖扩展方法将某些东西包装成一个事务

    public static void WrapInTransaction(this DbContext db, Action something)
    {
        if (db.Database.CurrentTransaction != null)
            something();
        else
            using (db.Database.BeginTransaction())
            {
                something();
            }
    }

答案 2 :(得分:0)

在EF6之前,提供更大范围事务的推荐方法是使用TransactionScope对象,该对象仍然有效,但似乎已弃用(请参阅here - 页面底部)