TransactionScope在处理完毕前中止了事务。

9
使用TransactionScope时,如果内部执行的代码回滚了事务,那么父事务也会回滚,这对我来说是好的。但是,当处理该范围时,它会抛出异常,意味着事务已经被回滚并中止了。那么正确的处理方式是什么,如何正确地处理该范围?
    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
    {
                    using (var conn = GetConnection())
                    {
                            string query = 
              @"some query that may contain transaction itself 
              or some SP whith transaction included"

                            using (var command = new SqlCommand(query, conn))
                                command.ExecuteNonQuery();
                        }
                    }
                    scope.Complete();
    } // Exception here
3个回答

9

scope.Dispose()可能会抛出TransactionAborted异常,即使已调用scope.Complete()。例如,一些存储过程足够聪明,可以使用T-SQL TRY/CATCH结构在T-SQL脚本中处理异常并中止事务,而不会向调用方抛出异常。

因此,我建议最安全的方法如下:

try
{
    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
    {
        try
        {
            using (var conn = GetConnection())
            {
                string query = 
                @"some query that may contain transaction itself 
                or some SP whith transaction included"

                using (var command = new SqlCommand(query, conn))
                command.ExecuteNonQuery();
            }
        }
        catch (SqlException ex)
        {
            // log SQL Exception, if any
            throw;  // re-throw exception
        }

        scope.Complete();
    }
}
catch (TransactionAbortedException ex)
{
    // we can get here even if scope.Complete() was called.
    // log TransactionAborted exception if necessary
}

不用担心如何释放 TransactionScope。scope.Dispose 方法会在抛出 TransactionAborted 异常之前执行必要的清理操作。


3

我不使用存储过程或SQL特定的try/catch,所以我的情况略有不同,但我遇到了与帖子中提到的完全相同的事务中止异常。我发现,如果在主要的TransactionScope内部有一个SELECT语句,那么这将导致事务提交,尽管我不确定原因。我有一个情况,在创建数据库中的对象之前,它首先通过SELECT检查对象是否已经存在,然后在调用Dispose时发生了中止异常。我查看了内部异常,它说事务试图在没有开始的情况下提交。最后,我尝试将我的SELECT语句包装在被抑制的TransactionScope中,然后它就可以工作了。所以:

using(TransactionScope tx = new TransactionScope()) 
{ 
  //UPDATE command for logging that I want rolled back if CREATE fails

  using(TransactionScope tx2 = new TransactionScope(TransactionScopeOption.Suppress)) 
  { 
    // SELECT command
  } 

  //If not exists logic
  //CREATE command
} //Used to error here, but not with the SELECT Suppressed

我希望这篇文章能够帮助到其他没有使用存储过程却遇到同样异常情况的人。

0
如果您的内部查询抛出异常,则scope.Complete()行将不会执行。 请参考下面的链接。我还对您的查询进行了一些更改,希望它能为您工作。 事务范围
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
    using (var conn = GetConnection())
    {
        string query = 
        @"some query that may contain transaction itself 
        or some SP whith transaction included"

        using (var command = new SqlCommand(query, conn))
        command.ExecuteNonQuery();
    }
    scope.Complete();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接