显式调用事务回滚或让异常触发隐式回滚是更好的做法吗? [英] Is it a better practice to explicitly call transaction rollback or let an exception trigger an implicit rollback?

查看:59
本文介绍了显式调用事务回滚或让异常触发隐式回滚是更好的做法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,如果在执行 SQL 语句时抛出任何异常,我们应该期待事务的隐式回滚,因为事务未提交,它超出范围并被处理:

In the below code if any exception is thrown while executing the the SQL statements we should expect an implicit rollback on the transaction as the transaction was not committed, it goes out of scope and it gets disposed:

using (DbTransaction tran = conn.BeginTransaction())
{
    //
    // Execute SQL statements here...
    //
    tran.Commit();
}

以上是一种可接受的做法,还是应该捕获异常并显式调用 tran.Rollback(),如下所示:

Is the above an acceptable practice, or should one catch the exception and explicitly make a call to tran.Rollback() as shown below:

using (DbTransaction tran = conn.BeginTransaction())
{
    try
    {
        //
        // Execute SQL statements here...
        //
        tran.Commit();
    }
    catch
    {
        tran.Rollback();
        throw;
    }
}

推荐答案

前者.如果您查找类似主题的 MSDN 示例,例如 TransactionScope,他们都赞成隐式回滚.造成这种情况的原因有多种,但我只给您一个非常简单的原因:当您捕获异常时,事务可能已经回滚.许多错误会回滚挂起的事务,然后将控制权返回给客户端,其中 ADO.Net 引发 CLR SqlException 事务已经在服务器上回滚 (1205DEADLOCK 是此类错误的典型示例),因此显式 Rollback() 调用充其量是无操作,更糟的是错误.DbTransaction(例如SqlTransaction)的提供者应该知道如何处理这种情况,例如.因为服务器和客户端之间有明确的聊天通知事务已经回滚的事实,并且 Dispose() 方法做了正确的事情.

Former. If you look up MSDN samples on similar topics, like TransactionScope, they all favor the implicit rollback. There are various reasons for that, but I'll just give you a very simple one: by the time you catch the exception, the transaction may had already rolled back. Many errors rollback the pending transaction and then they return control to the client, where the ADO.Net raises the CLR SqlException after the transaction was already rolled back on the server (1205 DEADLOCK is the typical example of such an error), so the explicit Rollback() call is, at best, a no-op, and at worse an error. The provider of the DbTransaction (eg. SqlTransaction) should know how to handle this case, eg. because there is explicit chat between the server and the client notifying of the fact that the transaction rolled back already, and the Dispose() method does the right thing.

第二个原因是事务可以嵌套,但 ROLLBACK 的语义是一次回滚回滚所有事务,所以你只需要调用一次(不像 Commit() 只提交最内部的事务,并且必须在每次开始时成对调用).同样,Dispose() 做正确的事.

A second reason is that transactions can be nested, but the semantics of ROLLBACK are that one rollback rolls back all transactions, so you only need to call it once (unlike Commit() which commits only the inner most transaction and has to be called paired up for each begin). Again, Dispose() does the right thing.

更新

SqlConnection.BeginTransaction() 实际上支持第二种形式,并在 catch 块中执行显式的 Rollback().我怀疑技术作者只是想在一个示例中展示 Rollback()Commit(),注意他如何需要在Rollback 以规避我最初提到的一些问题.

The MSDN sample for SqlConnection.BeginTransaction() actually favors the second form and does an explicit Rollback() in the catch block. I suspect the technical writer simply intended to show in one single sample both Rollback() and Commit(), notice how he needed to add a second try/catch block around the Rollback to circumvent exactly some of the problems I mentioned originally.

这篇关于显式调用事务回滚或让异常触发隐式回滚是更好的做法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆