手动TransactionScope和回滚事务之间的区别?

时间:2013-02-12 17:37:17

标签: c# sql-server sql-server-2008

我正在研究之前由其他人开发的项目。在程序中,它执行以下操作:

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

我的第二个问题是:如果我手动完成,如下所示。使用transactioncope的任何优势?它们字面上是一样的吗?

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

}

感谢。

1 个答案:

答案 0 :(得分:0)

SqlTransaction文档中包含了这一点:

  

Dispose应该回滚事务。但是,行为   Dispose是特定于提供者的,不应该替换调用Rollback。

取自: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();