2013-10-11 28 views
2

我有一個應用程序,我想將幾​​個數據庫保存到單個事務中。如果它們中的任何一個失敗了,我想把整個事情推回去。但是,我想知道哪些在回滾事務之前失敗(或成功)。內部TransactionScope中的異常導致所有後續的內部TransactionScopes拋出TransactionAbortedException

我有一個內部循環的外部TransactionScope,其中循環的每個迭代都有自己的TransactionScope。我想運行它們並找出哪些失敗。

例如,如果我有5件事我想嘗試並保存,但第一個和第三個將失敗,我想知道這一點。這需要我嘗試所有5次保存,如果失敗了,那麼將所有事情都回滾,但只有在5次嘗試之後。

我看到雖然,第一次失敗的事務後,所有後續使用的TransactionScope立即拋出自己的TransactionAbortedException並不讓我試圖保存,看看它是否工作。

下面是一個例子:

using (var scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead}, EnterpriseServicesInteropOption.Full)) 
{ 
    var outputStatus = new List<string>(); 

    for (int i = 0 ; i < 5 ; i++) 
    { 
     try 
     { 
      using (var innerScope = new System.Transactions.TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.RepeatableRead}, EnterpriseServicesInteropOption.Full)) 
      { 
       // Do work here that causes an exception on first iteration only 
       if (i == 0) 
       { 
        throw new Exception(string.Format("Iteration {0} has FAILED", i)); 
       } 
       else 
       { 
        outputStatus.Add("SUCCESS"); 
       } 
      } 
     } 
     catch (Exception e) 
     { 
      outputStatus.Add("ERROR, " + e.Message); 
     } 
    } 

    // Print out outputStatus values here 
} 

在此代碼outputStatus集合看起來像這樣的結尾:

  • 錯誤,迭代0失敗
  • 錯誤,該交易已中止。
  • 錯誤,交易已中止。
  • 錯誤,交易已中止。
  • 錯誤,交易已中止。

第一個異常後,其餘的都沒有能夠進入成功的語句。

是否有辦法在外部事務範圍內運行所有內部事務並允許我控制外部事務範圍的回滾?

UPDATE:

在實際的代碼,這個樣本模仿我不能更改到包含內部的TransactionScope的代碼。它在一個不受我控制的物體中。所以我正在尋找的解決方案需要能夠處理內部事務拋出異常。

回答

1

在試圖模擬這個我自己,我發現你不能這樣做,或我最初提出的建議。如果您無論如何都將事務範圍關聯起來,並且其中一個未正確完成,則後續對構造函數的調用將僅導致異常並中止。如果您嘗試手動更改它們或將它們嵌套在一起而不與它們相關,那麼在完成後Dispose()。將拋出一個異常,說您嵌套不正確或Transaction.Current在範圍內發生了更改。

在我看來,你必須選擇有一個交易,或獨立嘗試所有這些事情之間進行選擇,並檢查它失敗並糾正之後。

最後我發現(通過使用JetBrains dotPeek)事務具有線程關聯性。您可以通過在不同的線程上進行管理來管理您的5個呼叫。當然,你將不得不使用某種障礙http://en.wikipedia.org/wiki/Synchronous_rendezvous,以防止任何線程完成,直到所有線程完成。如果它們是順序的,則必須使用額外的同步構造才能使它們按順序執行。

請記住,這不會是原子性的,在您決定要完成所有事務後,它們可能仍然出錯!他們畢竟是獨立的。 如果你不小心,你可能會被鎖定,這取決於你真正的工作應該做什麼。 如果您的資源分散在不同的機器或數據庫中,這可能無法發揮出色,這會增加您的應用程序發佈完成的可能性,但遠程資源會以其他方式做出決定。

原來的答案:

你應該明白你的例外內(循環)結束之前利用。

閱讀上(注):http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx

如果事務範圍內未發生異常(即,TransactionScope的對象的初始化及其Dispose方法的調用之間),那麼事務中範圍參與允許繼續。 如果在事務處理範圍內發生異常,它將參與的事務將被回滾。

我建議你閱讀這篇文章,以及:當你想保留的代碼段執行的操作http://msdn.microsoft.com/en-us/library/ms172152.aspx

禁止是有益的,並且不希望中止環境事務如果操作失敗。例如,當您想執行日誌記錄或審計操作時,或者當您想要將事件發佈給訂閱者時,無論您的環境事務是提交還是中止。此值允許您在事務範圍內具有非事務代碼部分,如以下示例所示。

補充:我一直在閱讀msdn,我認爲如果您創建另一個級別的事務,您可能會這樣做。 我的理由是:

  • 您的交易失敗,因爲範圍(最外層),你控制的是根事務。
  • 您不控制的代碼確實要求交易,但不要求新交易(參數TransactionScopeOption.Required。),這意味着該庫內部外部是正在運行的交易,如果失敗,其他操作將失敗。
  • 爲了防止這種情況發生,您可以在失去對外部代碼的控制之前創建另一個範圍。但確保你要求一個RequiredNew範圍。這將隔離你不能控制的代碼,並給你一個機會來捕捉這個異常。

我修改後的解決方案是類似的。

using (var scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required)) 
    { 
var outputStatus = new List<string>(); 

for (int i = 0 ; i < 5 ; i++) 
{ 
    //Note RequiredNew, rest of the arguments suppressed 
    using (var innerScope = new System.Transactions.TransactionScope(TransactionScopeOption.RequiredNew)) 
    { 
     try 
     { 

      // Do work here that causes an exception on first iteration only <-- is this really the case or is just an example, if so could you skip the first one? 
      SomeService.DoSOmetaskWhichUsesATransactionInsideOfIt(i); 
      outputStatus.Add("SUCCESS : " + i); 
          innerScope.Complete(); 

     } 
     catch (Exception e) 
     { 
      outputStatus.Add("ERROR, " + i + " " + e.Message); 
     } 
    } 

} 
// IN here you must inspect outputStatus and decide if you want to complete the transaction (all of it , or the parts that didn't fail) or not. 
if(/* all good */) { 
    scope.Complete(); 
} 
// Print out outputStatus values here 
} 

如果不適合,例如,你需要你可能需要明確地考慮更先進的交易主題,並做到這一點。 我建議你閱讀:http://msdn.microsoft.com/en-us/library/ms172146.aspx 這超出了我對交易的理解,所以我不太確定如何應用它。

+0

感謝您的回覆。我實際上沒有對內部TransactionScope調用的任何控制。在真實的代碼中(這個例子模仿)控制內部事務範圍的對象是我沒有更新權限的對象。我已經使用該信息更新了我的問題。 – Grandpappy

+0

我看到您的更新,但不會將RequiresNew事務從外部事務中排除,因爲它會創建一個新事務?這將使我無法將所有RequiredNew TransactionScopes回滾,是否正確? – Grandpappy

+0

老實說,我不清楚它會做什麼(我會試試看)。如果您搜索此網站以使用requiredNew,則看起來您是正確的,innerTransaction完全獨立(並且令人困惑)。如果是這種情況,你將不得不自己管理所有的交易,而不是使用使用的代碼塊,而是使用try-catch-finally和靜態Transaction.Current或ctor TransactionScope(TransactionToUse)。請注意,在Dispose被調用之前,沒有任何事情可以完成或撤消。 – Jorge

相關問題