在下面的代码中,如果在执行sql语句时抛出任何异常,我们应该期待事务的隐式回滚,因为事务没有提交,它超出了范围,它被处理:
using (DbTransaction tran = conn.BeginTransaction()) { // // Execute sql statements here... // tran.Commit(); }
以上是可接受的做法,或者应该捕获异常并明确地调用tran.Rollback(),如下所示:
using (DbTransaction tran = conn.BeginTransaction()) { try { // // Execute sql statements here... // tran.Commit(); } catch { tran.Rollback(); throw; } }
解决方法
前任的.如果您查找类似主题的MSND样本,例如
TransactionScope
,他们都赞成隐式回滚.有各种各样的原因,但我会给你一个非常简单的一个:当你捕获异常时,事务可能已经回滚了.许多错误回滚挂起的事务,然后将控制权返回给客户端,ADO.Net在事务已经在服务器上回滚之后引发了CLR sqlException(1205 DEADLOCK是这种错误的典型示例),因此显式Rollback()调用最多只是一个no-op,更糟糕的是一个错误. DbTransaction的提供者(例如sqlTransaction)应该知道如何处理这种情况,例如.因为服务器和客户端之间有明确的聊天,通知事务已经回滚,Dispose()方法做正确的事情.
第二个原因是事务可以嵌套,但是ROLLBACK的语义是一个回滚可以回滚所有事务,所以你只需要调用它一次(不同于Commit(),它只提交内部最多的事务,并且必须被配对每个开始).再次,Dispose()做正确的事情.
更新
SqlConnection.BeginTransaction()
的MSDN示例实际上有利于第二种形式,并在catch块中显式显示Rollback().我怀疑技术作者只是想在一个单一的样本中显示Rollback()和Commit(),注意到他需要如何在Rollback周围添加一个第二个try / catch块,以规避我最初提到的一些问题.