简体   繁体   English

实体框架6事务回滚

[英]Entity Framework 6 transaction rollback

With EF6 you have a new transaction which can be used like: 使用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();
                }
            }
        }

Is rollback actually needed when things go sideways? 当事情横盘整理时,是否真的需要回滚? I'm curious because the Commit description says: "Commits the underlying store transaction." 我很好奇,因为“提交”说明中说:“提交基础商店交易。”

Whereas the Rollback description says: "Rolls back the underlying store transaction." 而“回滚”说明说:“回滚基础商店交易。”

This makes me curious, because it looks to me that if Commit isn't called, the previously executed commands will not be stored (which seems logical to me). 这让我感到好奇,因为在我看来,如果未调用Commit,先前执行的命令将不会被存储(对我来说这是合乎逻辑的)。 But if that is the case, what would the reason be to call the Rollback function? 但是如果是这种情况,调用Rollback函数的原因是什么? In EF5 I used TransactionScope which didn't have a Rollback function (only a Complete) which seemed logical to me. 在EF5中,我使用了TransactionScope,它没有回滚功能(只有完整功能),这对我来说似乎很合理。 Due to MS DTC reasons I cannot use the TransactionScope anymore, but I also cannot use a try catch like the example above (ie, I only need the Commit). 由于MS DTC的原因,我不能再使用TransactionScope,但是也不能像上面的示例那样使用try catch(即,我只需要Commit)。

You don't need to call Rollback manually because you are using the using statement. 您不需要手动调用Rollback ,因为您正在使用using语句。

DbContextTransaction.Dispose method will be called in the end of the using block. DbContextTransaction.Dispose方法将在using块的末尾调用。 And it will automatically rollback the transaction if the transaction is not successfully committed (not called or encountered exceptions). 如果未成功提交事务(未调用或遇到异常),它将自动回滚事务。 Following is the source code of SqlInternalTransaction.Dispose method ( DbContextTransaction.Dispose will finally delegate to it when using SqlServer provider): 以下是SqlInternalTransaction.Dispose方法的源代码(使用SqlServer提供程序时, DbContextTransaction.Dispose将最终委托给它):

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

You see, it checks if _innerConnection is not null, if not, rollback the transaction (if committed, _innerConnection will be null). 您会看到,它检查_innerConnection是否不为null,否则检查回滚事务(如果已提交,则_innerConnection为null)。 Let's see what Commit does: 让我们看看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);
    }
}

As long as you will always be using SQL Server with EF, there is no need to explicitly use the catch to call the Rollback method. 只要您始终将SQL Server与EF一起使用,就无需显式使用catch来调用Rollback方法。 Allowing the using block to automatically rollback on any exceptions will always work. 允许using块自动回滚任何异常将始终有效。

However, when you think about it from the Entity Framework point of view, you can see why all examples use the explicit call to Rollback the transaction. 但是,从实体框架的角度考虑它时,您可以看到为什么所有示例都使用显式调用来回滚事务。 To the EF, the database provider is arbitrary and pluggable and the provider can be replaced with MySQL or any other database that has an EF provider implementation. 对于EF,数据库提供程序是任意的且可插入的,并且可以使用MySQL或具有EF提供程序实现的任何其他数据库替换该提供程序。 Therefore, from the EF point of view, there is no guarantee that the provider will automatically rollback the disposed transaction, because the EF does not know about the implementation of the database provider. 因此,从EF的角度来看,由于EF不知道数据库提供程序的实现,因此不能保证提供程序将自动回滚已处置的事务。

So, as a best practice, the EF documentation recommends that you explicitly Rollback -- just in case you someday change providers to an implementation that does not auto-rollback on dispose. 因此,作为最佳实践,EF文档建议您显式回滚-以防万一有一天您将提供程序更改为在处置时不会自动回滚的实现。

In my opinion, any good and well written provider will automatically rollback the transaction in the dispose, so the additional effort to wrap everything inside the using block with a try-catch-rollback is overkill. 在我看来,任何优秀且编写良好的提供程序都将自动在Dispose中回滚事务,因此用try-catch-rollback将所有内容包装在using块内的额外努力是过大的。

  1. Since you have written a 'using' block to instantiate the transaction, you do not need to mention the Rollback function explicitly since it would be automatically rolled back (unless it was committed) at the time of disposal. 由于您已经编写了一个“使用”块来实例化事务,因此您无需显式提及Rollback函数,因为它将在处置时自动回滚(除非已提交)。
  2. But if you instantiate it without a using block, in that case it is essential to rollback the transaction in case of an exception (precisely in a catch block) and that too with a null check for a more robust code. 但是,如果您在不使用using块的情况下实例化它,则在这种情况下,必须在发生异常(准确地在catch块中)的情况下回滚事务,并且对于空健的代码也必须进行空检查。 The working of BeginTransaction is unlike transaction scope (which just needs a complete function if all the operations were completed successfully). BeginTransaction的工作方式不同于事务范围(如果成功完成所有操作,则只需要一个完整的功能)。 Instead, it is akin to the working of Sql transactions. 相反,它类似于Sql事务的工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM