2009-04-24 93 views
3

我試圖做一個快速的虛擬應用程序,所以我可以瞭解System.Transactions的來龍去脈。這個應用程序與2個不同的SQLExpress數據庫進行交互。如果我在組件服務中提取事務統計信息,則可以在打開第二個連接時在outerScope中看到事務啓動。如果failOuter爲true,則事務中止,但不會拋出任何異常。當failInner爲true時,會引發TransactionAbortedException。試圖瞭解TransactionScope

從MSDN:

當你的應用程序完成所有它想要在交易完成工作,你應該調用Complete方法只有一次告知事務管理器,它是可以接受的提交事務。將調用完成爲使用塊中的最後一條語句是非常好的做法。

無法調用此方法會中止事務,因爲事務管理器將此解釋爲系統故障,或等同於事務範圍內引發的異常。

如果作用域創建事務並且事務被中止,則會引發TransactionAbortedException。

基於這一點,我期望我的outerScope拋出一個TransactionAbortedException,因爲我的事務統計顯示一箇中止事務每次我運行我的應用程序與failOuter設置爲true。我的方法返回true,因爲即使事務中止也不會拋出異常。除非我放棄內部交易,否則它的行爲如我所料。任何澄清將不勝感激。

public bool CreateNestedTransaction(bool failOuter, bool failInner) 
    { 
     try 
     { 
      using (TransactionScope outerScope = new TransactionScope()) 
      { 

       /* Perform transactional work here */ 
       using (SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=test1")) 
       { 
        SqlCommand myCommand = new SqlCommand(); 
        myConnection.Open(); 
        myCommand.Connection = myConnection; 

        myCommand.CommandText = "update test set Value = ((select Value from test where Id = (select max(Id) from test))+1) where Id = (select max(Id) from test)"; 
        myCommand.ExecuteNonQuery(); 
       } 


       using (SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=test1")) 
       { 
        SqlCommand myCommand = new SqlCommand(); 
        myConnection.Open(); 
        myCommand.Connection = myConnection; 

        myCommand.CommandText = "update test set Value = Value"; 
        myCommand.ExecuteNonQuery(); 
       } 

       using (TransactionScope innerScope = new TransactionScope()) 
       { 
        using (SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=test2")) 
        { 
         SqlCommand myCommand = new SqlCommand(); 
         myConnection.Open(); 
         myCommand.Connection = myConnection; 

         myCommand.CommandText = "update test set Value = ((select Value from test where Id = (select max(Id) from test))+1) where Id = (select max(Id) from test)"; 
         myCommand.ExecuteNonQuery(); 
        } 
        if (failInner == false) { innerScope.Complete(); } 
       } 

       if (failOuter == false) { outerScope.Complete(); } 
      } 
     } 

     catch (TransactionAbortedException) 
     { 
      return false; 
     } 

     return true; 
    } 

回答

7

通常你不會由於未能調用TransactionScope.Complete()的TransactionScope的超出範圍之前得到處理拋出的異常。交易將會悄悄回滾。

由於您試圖在外部TransactionScope上調用Complete,並且無法正確完成,因爲內部TransactionScope已失敗 - 因此會引發異常,您的情況中發生異常。

這有道理嗎?

如果你想進行一些操作,如果你的外部事務中止,你可以嘗試這樣的事:內

// Inside each using TransactionScope(), hhok up the current transaction completed event 
Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(Current_TransactionCompleted); 

// handle the event somewhere else 
void Current_TransactionCompleted(object sender, TransactionEventArgs e) 
{ 
    // check the status of the transaction 
    if(e.Transaction.TransactionInformation.Status == TransactionStatus.Aborted) 
    // do something here 
} 

雖然我覺得對於一般的使用更清潔的方式將始終調用完成()你TransactionScope並處理任何產生的異常,如果你想做一些特定的事務失敗。

+0

是的,這是有道理的。我認爲MSDN的最後一行是把我拋棄。我正在看我的交易中止,並期待一個例外。感謝您的解釋!有沒有辦法判斷我的外部異常是否中止?我想在這種情況下返回false。 – 2009-04-24 18:57:47