TransactionScope和手动回滚事务之间的差异

本文关键字:之间 回滚事务 TransactionScope | 更新日期: 2023-09-27 17:58:54

我正在做一个以前由其他人开发的项目。在程序中,它会做这样的事情:

 reportConnection = new SqlConnection(sConnStr);
 reportConnection.Open();
 try
 {
     using (SqlTransaction trans = reportConnection.BeginTransaction(IsolationLevel.ReadUncommitted))                    
     {
        ......
        using (SqlCommand cmd = new SqlCommand(sSQL, reportConnection, trans))
        {
            ....
        }
        trans.Commit();
     }
}
catch (Exception ex)
{
    Logger ....
}
fianlly
{
    if (reportConnection != null)
    {
        ((IDisposable)reportConnection).Dispose();
    }
}

我看到的问题是,如果using块中出现错误,它不会回滚事务。因此,这里是第一个问题:如果出现错误,trans将不会提交(也不会回滚),但连接将被处理(trans不会被处理)。在这种情况下,它会产生什么副作用?它会创建一个孤立的连接/事务吗?那么,会造成死锁吗?

我做了一些搜索,似乎首选的方法是使用transactionscope(下面的代码来自microsoft):

try
{
    // Create the TransactionScope to execute the commands, guaranteeing 
    // that both commands can commit or roll back as a single unit of work. 
    using (TransactionScope scope = new TransactionScope())
    {
        using (SqlConnection connection1 = new SqlConnection(connectString1))
        {
            // Opening the connection automatically enlists it in the  
            // TransactionScope as a lightweight transaction.
            connection1.Open();
            // Create the SqlCommand object and execute the first command.
            SqlCommand command1 = new SqlCommand(commandText1, connection1);
            returnValue = command1.ExecuteNonQuery();
            writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
            // If you get here, this means that command1 succeeded. By nesting 
            // the using block for connection2 inside that of connection1, you 
            // conserve server and network resources as connection2 is opened 
            // only when there is a chance that the transaction can commit.    
            using (SqlConnection connection2 = new SqlConnection(connectString2))
            {
                // The transaction is escalated to a full distributed 
                // transaction when connection2 is opened.
                connection2.Open();
                // Execute the second command in the second database.
                returnValue = 0;
                SqlCommand command2 = new SqlCommand(commandText2, connection2);
                returnValue = command2.ExecuteNonQuery();
                writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
            }
        }
        // The Complete method commits the transaction. If an exception has been thrown, 
        // Complete is not  called and the transaction is rolled back.
        scope.Complete();
    }
}
catch (TransactionAbortedException ex)
{
    writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
}
catch (ApplicationException ex)
{
    writer.WriteLine("ApplicationException Message: {0}", ex.Message);
}

我的第二个问题是:如果我像下面这样手动操作会怎么样。使用transactionscope有什么好处吗?它们真的一样吗?

reportConnection = new SqlConnection(sConnStr);
reportConnection.Open();
try
 {
     using (SqlTransaction trans = reportConnection.BeginTransaction(IsolationLevel.ReadUncommitted))                    
     {
        try
        {
            ......
            using (SqlCommand cmd = new SqlCommand(sSQL, reportConnection, trans))
            {
                ....
            }
            trans.Commit();
        }
        catch
        {
            try
            {
                // incase rollback has error
                trans.Rollback();
            }
            catch
            {
            }
        }
     }
}
catch (Exception ex)
{
    Logger ....
}
fianlly
{
    if (reportConnection != null)
    {
        ((IDisposable)reportConnection).Dispose();
    }
}

谢谢。

TransactionScope和手动回滚事务之间的差异

SqlTransaction文档中介绍了这一点:

Dispose应回滚事务。然而Dispose是特定于提供程序的,不应取代调用回滚。

取自:http://msdn.microsoft.com/en-us/library/bf2cw321.aspx

我想这完全取决于你有多偏执,当你认为坏的实现可能无法正常工作时,你会试图强迫它们正常工作。第一个代码示例绝对是最简单的,只有当您的提供者不能正常工作时,它才会失败。

编辑复杂性示例-您的最后一个代码片段应该是:

try { /*...*/ 
    using (SqlCommand cmd = new SqlCommand(sSQL, reportConnection, trans)) {
        /*...*/ }
    trans.Commit();
} catch { try { trans.Rollback(); } catch {} throw; }

try { /*...*/ 
    using (SqlCommand cmd = new SqlCommand(sSQL, reportConnection, trans)) {
        /*...*/ }
} catch { try { trans.Rollback(); } catch {} throw; }
trans.Commit();