2010-04-05 93 views
2

對catch子句中已存在的SqlTransaction RollBack實現錯誤處理的最佳方式是什麼?我的代碼大致是這樣的:如何在RollBack上捕獲異常

using (SqlConnection objSqlConn = new SqlConnection(connStr)) { 
    objSqlConn.Open(); 

    using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) { 
    try { 
     // code 
     // more code 
     // and more code 
    } 
    catch (Exception ex) { 
     // What happens if RollBack() has an exception? 
     objSqlTrans.Rollback(); 
     throw ex; 
    } 
    } 
} 

我相信我的應用程序必須在try塊,而這又被夾在catch塊,然後回滾試圖在一個例外。但是,我看到的錯誤說了一些關於SqlTransaction.ZombieCheck()的問題,這讓我懷疑RollBack()本身是否也拋出了異常。那麼,我需要在RollBack()中實現某種類型的錯誤處理嗎?我該怎麼做,並設法保留將執行放入catch塊的異常呢?

編輯 - 我的所有代碼:

using (SqlConnection objSqlConn = new SqlConnection(connStr)) { 

    objSqlConn.Open(); 

    // Begin Transaction 
    using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) { 

     try { 
      // Create file in db (which in turn creates it on disk according to where the 
      // ...FileStream points) 
      SqlCommand objSqlCmd = new SqlCommand("usp_FileAdd", objSqlConn, objSqlTrans); 
      objSqlCmd.CommandType = CommandType.StoredProcedure; 

      // Sql parameter - report name 
      SqlParameter objSqlParam1 = new SqlParameter("@ObjectID", SqlDbType.Int); 
      objSqlParam1.Value = objID; 

      // Sql out parameter - returns the file path 
      SqlParameter objSqlParamOutput = new SqlParameter("@filepath", SqlDbType.VarChar, -1); 
      objSqlParamOutput.Direction = ParameterDirection.Output; 

      // Add Sql parameters to command obj 
      objSqlCmd.Parameters.Add(objSqlParam1); 
      objSqlCmd.Parameters.Add(objSqlParamOutput); 

      // Execute command object 
      objSqlCmd.ExecuteNonQuery(); 

      // Path to the FileStream 
      string path = objSqlCmd.Parameters["@filepath"].Value.ToString(); 

      // Reset command object to get FileStream 
      objSqlCmd = new SqlCommand(
       "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", 
       objSqlConn, 
       objSqlTrans); 

      // Execute command object 
      Object obj = objSqlCmd.ExecuteScalar(); 

      if (obj != DBNull.Value) { 
       // Byte array representing the FileStream 
       byte[] fsBytes = (byte[])obj; 

       SqlFileStream sqlFS = new SqlFileStream(path, fsBytes, FileAccess.Write); 

       using (FileStream fs = fi.OpenRead()) { 
        //byte[] b = new byte[1024]; 
        byte[] b = new byte[4096]; 
        int read; 

        fs.Seek(0, SeekOrigin.Begin); 

        while ((read = fs.Read(b, 0, b.Length)) > 0) { 
         sqlFS.Write(b, 0, read); 
        } 
       } 

       sqlFS.Close(); 
      } 

      // Commit the transaction 
      objSqlTrans.Commit(); 
     } 
     catch (Exception ex) { 
      objSqlTrans.Rollback(); 
      throw ex; 
     } 
    } 
} 

回答

1

你已經有了

using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) 

這將導致事務回滾時,如果它沒有被COMMITED using塊結束。

所以我會完全刪除catch塊。

至於回滾失敗時會發生什麼,我會首先認識到這是一個非常糟糕的情況,並遵循Eric Lippert關於類似問題的建議。 here

+0

我想你可能已經釘住了它。我在SO上發現了另一個關於使用SqlTransaction塊處理的問題,並且我注意到他們的例子中他們也沒有使用Rollback。我不認爲我的問題是該問題的重複。 http://stackoverflow.com/questions/1127830/why-use-a-using-statement-with-a-sqltransaction – Jagd 2010-04-05 17:18:49

+0

確定回滾將嘗試沒有顯式,但它會保留內部異常,而不是拋出一個異常沒有更可能的原因? – Maslow 2014-10-24 14:57:52

+0

@Maslow使用語句不會影響異常中的內容。所以如果有一個由SqlCommand.ExecuteScalar提供的內部異常,它將被保留。 – 2014-10-24 17:43:19

1

該片段應閱讀像這樣:

using (SqlConnection objSqlConn = new SqlConnection(connStr)) { 
objSqlConn.Open(); 

using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) { 
    try { 
    // code 
    // more code 
    // and more code 
    } 
    catch (Exception ex) { 
    // What happens if RollBack() has an exception? 
    try { 
     objSqlTrans.Rollback(); 
    } catch (Exception ex2) { 
     /* can't roll back -- db gone? db will do it for us since we didn't commit. */ 
    } 
    throw; 
    } 
} 
} 

編輯:試想想,它沒有必要對整個try/catch語句在這種特殊情況下,由於關閉與未提交的事務的連接會回滾事務,因此該塊可能如下所示:

using (SqlConnection objSqlConn = new SqlConnection(connStr)) { 
objSqlConn.Open(); 

using (SqlTransaction objSqlTrans = objSqlConn.BeginTransaction()) { 
    // code 
    // more code 
    // and more code 
} 
} 
+0

我想你錯過了第二個try塊的catch關鍵字。有一件事讓我感到困惑 - 如果Rollback()工作正常並且沒有錯誤,那麼我將永遠不會看到原始異常,因爲拋出只存在於第二個catch塊中,對吧? – Jagd 2010-04-05 16:54:54

+0

固定的換行符使得它顯而易見的是throw在第一個catch塊的末尾。 – Joshua 2010-04-05 17:49:32