实体框架6事务回滚

The*_*Dog 73 c# sql-server entity-framework transactions entity-framework-6

使用EF6,您可以使用以下新事务:

using (var context = new PostEntityContainer())
        {
            using (var dbcxtransaction = context.Database.BeginTransaction())
            {
                try
                {
                    PostInformation NewPost = new PostInformation()
                    {
                        PostId = 101,
                        Content = "This is my first Post related to Entity Model",
                        Title = "Transaction in EF 6 beta"
                    };
                    context.Post_Details.Add(NewPost);
                    context.SaveChanges();
                    PostAdditionalInformation PostInformation = new PostAdditionalInformation()
                    {
                        PostId = (101),
                        PostName = "Working With Transaction in Entity Model 6 Beta Version"
                    };

                    context.PostAddtional_Details.Add(PostInformation);
                    context.SaveChanges();

                    dbcxtransaction.Commit();
                }
                catch
                {
                    dbcxtransaction.Rollback();
                }
            }
        }
Run Code Online (Sandbox Code Playgroud)

当事情横盘整理时,实际上需要回滚吗?我很好奇,因为提交描述说:"提交基础商店交易."

而回滚描述说:"回滚基础商店交易."

这让我感到很好奇,因为在我看来,如果没有调用Commit,先前执行的命令将不会被存储(这对我来说似乎是合乎逻辑的).但如果是这种情况,那么调用Rollback函数的原因是什么?在EF5中我使用了TransactionScope,它没有回滚功能(只有一个完整的),这对我来说似乎是合乎逻辑的.由于MS DTC原因,我不能再使用TransactionScope,但我也不能像上面的例子一样使用try catch(即,我只需要Commit).

Mou*_*Lin 108

您不需要Rollback手动调用,因为您正在使用该using语句.

DbContextTransaction.Dispose方法将在using块的末尾调用.如果事务未成功提交(未调用或遇到异常),它将自动回滚事务.以下是SqlInternalTransaction.Dispose方法的源代码(DbContextTransaction.Dispose最终在使用SqlServer提供程序时委托给它):

private void Dispose(bool disposing)
{
    // ...
    if (disposing && this._innerConnection != null)
    {
        this._disposing = true;
        this.Rollback();
    }
}
Run Code Online (Sandbox Code Playgroud)

你看,它检查是否_innerConnection为null,如果不是,则回滚事务(如果已提交,则为_innerConnectionnull).让我们看看是Commit做什么的:

internal void Commit() 
{
    // Ignore many details here...

    this._innerConnection.ExecuteTransaction(...);

    if (!this.IsZombied && !this._innerConnection.IsYukonOrNewer)
    {
        // Zombie() method will set _innerConnection to null
        this.Zombie();
    }
    else
    {
        this.ZombieParent();
    }

    // Ignore many details here...
}

internal void Zombie()
{
    this.ZombieParent();

    SqlInternalConnection innerConnection = this._innerConnection;

    // Set the _innerConnection to null
    this._innerConnection = null;

    if (innerConnection != null)
    {
        innerConnection.DisconnectTransaction(this);
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 22

只要您始终使用带有EF的SQL Server,就不需要显式使用catch来调用Rollback方法.允许using块自动回滚任何异常将始终有效.

但是,当您从实体框架的角度考虑它时,您可以看到为什么所有示例都使用显式调用来回滚事务.对于EF,数据库提供程序是任意且可插入的,并且可以用MySQL或具有EF提供程序实现的任何其他数据库替换提供程序.因此,从EF的角度来看,不能保证提供者会自动回滚已处理的事务,因为EF不知道数据库提供者的实现.

因此,作为最佳实践,EF文档建议您显式回滚 - 以防万一有一天您将提供程序更改为不会在dispose上自动回滚的实现.

在我看来,任何好的和写得很好的提供程序都会自动回滚dispose中的事务,因此使用try-catch-rollback将所有内容包装在using块中的额外工作是过度的.


小智 5

  1. 由于您已经编写了一个 'using' 块来实例化事务,您无需明确提及 Rollback 函数,因为它会在处理时自动回滚(除非已提交)。
  2. 但是,如果您在没有 using 块的情况下实例化它,在这种情况下,必须在发生异常(恰好在 catch 块中)的情况下回滚事务,并且还需要对更健壮的代码进行空检查。BeginTransaction 的工作不同于事务范围(如果所有操作都成功完成,它只需要一个完整的函数)。相反,它类似于 Sql 事务的工作方式。