TransactionScope在处置之前中止了交易

时间:2014-01-23 15:14:45

标签: c# sql transactions

当使用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 个答案:

答案 0 :(得分:6)

即使scope.Dispose()被调用,

TransactionAborted也可能会抛出scope.Complete()例外。例如,一些足够聪明的存储过程可以使用T-SQL TRY/CATCH构造w/o向调用者抛出异常来处理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异常之前执行任何必要的清理工作。

答案 1 :(得分:0)

如果从内部查询抛出异常,则scope.Complete()行将不会执行。 请参考下面的链接..我也对您的查询进行了一些更改。我希望它适合你。 Transaction Scope

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();
}

答案 2 :(得分:0)

我不使用存储过程或特定于SQL的try / catch,所以我的情况略有不同,但我得到了帖子中提到的完全相同的事务中止异常。我发现如果我在主TransactionScope内部有一个SELECT,那将导致事务提交,虽然我不知道为什么。我有一个案例,为了在数据库中创建一个对象,它首先检查以确保该对象不存在SELECT,然后在调用Dispose时发生中止异常。我查看了内部异常,它说交易试图在没有开始的情况下提交。我终于尝试在Suppressed TransactionScope中包装我的SELECT,然后它工作了。所以:

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

我希望这可以帮助其他可能在不使用存储过程的情况下获得此异常的人。