2

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

 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 不会被释放)。在这种情况下,它会产生什么副作用?它会创建一个开放的孤立连接/事务吗?这样,会不会造成死锁?

我做了一些搜索,似乎首选的方法是使用事务范围(下面的代码来自微软):

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

我的第二个问题是:如果我像下面这样手动操作会怎样。使用事务范围有什么好处?它们在字面上是一样的吗?

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

}

谢谢。

4

1 回答 1

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

?

于 2013-02-12T18:03:30.393 回答