轉自MSDN:
類提供一個簡單方法,通過這一方法,您不必與事務本身互動,即可將代碼塊標記為參與某個事務。事務範圍可以自動選擇和管理環境事務。由於它便於使用並且效率很高,因此建議您在開發事務應用程式時使用 TransactionScope 類。
此外,您不必顯式向事務登記資源。任何 資源管理員(例如 SQL Server 2005)都可以檢測到該範圍建立的環境事務的存在並自動登記。
建立事務範圍
下面的樣本說明 TransactionScope 類的簡單用法。
| Visual Basic |
複製代碼 |
Using scope As TransactionScope = New TransactionScope() 'Create and open the SQL connection. The work done on this connection will be a part of the transaction created by the TransactionScope Dim myConnection As New SqlConnection("server=(local)\SQLExpress;Integrated Security=SSPI;database=northwind") Dim myCommand As New SqlCommand() myConnection.Open() myCommand.Connection = myConnection 'Restore database to near it's original condition so sample will work correctly. myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)" myCommand.ExecuteNonQuery() 'Insert the first record. myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')" myCommand.ExecuteNonQuery() 'Insert the second record. myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')" myCommand.ExecuteNonQuery() myConnection.Close() 'Call complete on the TransactionScope or not based on input Dim c As ConsoleKeyInfo While (True) Console.Write("Complete the transaction scope? [Y|N] ") c = Console.ReadKey() Console.WriteLine() If (c.KeyChar = "Y") Or (c.KeyChar = "y") Then scope.Complete() Exit While ElseIf ((c.KeyChar = "N") Or (c.KeyChar = "n")) Then Exit While End If End While End Using |
| C# |
複製代碼 |
using (TransactionScope ts = new TransactionScope()) { //Create and open the SQL connection. The work done on this connection will be a part of the transaction created by the TransactionScope SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=northwind"); SqlCommand myCommand = new SqlCommand(); myConnection.Open(); myCommand.Connection = myConnection; //Restore database to near it's original condition so sample will work correctly. myCommand.CommandText = "DELETE FROM Region WHERE (RegionID = 100) OR (RegionID = 101)"; myCommand.ExecuteNonQuery(); //Insert the first record. myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (100, 'MidWestern')"; myCommand.ExecuteNonQuery(); //Insert the second record. myCommand.CommandText = "Insert into Region (RegionID, RegionDescription) VALUES (101, 'MidEastern')"; myCommand.ExecuteNonQuery(); myConnection.Close(); //Call complete on the TransactionScope or not based on input ConsoleKeyInfo c; while (true) { Console.Write("Complete the transaction scope? [Y|N] "); c = Console.ReadKey(); Console.WriteLine(); if ((c.KeyChar == 'Y') || (c.KeyChar == 'y')) { // Commit the transaction ts.Complete(); break; } else if ((c.KeyChar == 'N') || (c.KeyChar == 'n')) { break; } } |
您建立了新的 TransactionScope 對象後,即開始事務範圍。 如程式碼範例中所示,建議您使用 using 語句建立範圍。C# 和 Visual Basic 中都提供 using 語句,其工作方式類似於 try...finally 塊,可確保正確處理範圍。
執行個體化 TransactionScope 時,交易管理員會確定要參與的事務。確定之後,該範圍將始終參與此事務。這個決定基於兩個因素:環境事務是否存在以及建構函式中 TransactionScopeOption 參數的值。環境事務是在其中執行代碼的事務。您可以通過調用 類的靜態 屬性,擷取對環境事務的引用。有關如何使用此參數的更多資訊,請參見本主題的一節。
完成事務範圍
在您的應用程式完成了它要在某一事務中執行的所有工作後,應該只調用 方法一次,以便通知交易管理員它可以提交該事務。強烈建議將對 Complete 的調用作為最後一條語句放置在 using 塊中。
如果調用此方法失敗,則會中止該事務,因為交易管理員會將此解釋為一個系統故障,或者與在事務範圍內引發的異常等效的故障。但是,調用此方法並不確保將提交該事務。這隻是一個向交易管理員通知您的狀態的方法。在調用 Complete 方法後,您將無法再通過 Current 屬性訪問環境事務,這樣做將會引發異常。
如果 TransactionScope 對象最初建立了該事務,則通過交易管理員提交該事務的實際工作將在 using 塊中的最後一行代碼後發生。如果它沒有建立該事務,則只要 對象的所有者調用了,就發生提交。在該時刻,交易管理員將基於對 TransactionScope 對象是否已調用了 Complete 方法,調用資源管理員並通知它們提交或復原。
using 語句確保調用 TransactionScope 對象的 方法,即使發生異常也是如此。Dispose 方法標記事務範圍的結束。在調用此方法後發生的異常不會影響該事務。該方法還將環境事務還原為其以前的狀態。
如果範圍建立該事務,則引發,並中止該事務。如果交易管理員無法達成提交決定,則引發。如果提交該事務,則不會引發任何異常。
復原事務
要復原某個事務,不應在該事務範圍內調用 Complete 方法。例如,您可以在該範圍內引發異常。該異常所參與的事務將被復原。
使用 TransactionScopeOption 管理事務流
通過在使用自己範圍的方法內調用使用 TransactionScope 的方法,可以嵌套事務範圍,下例中的 RootMethod 方法即如此。
| C# |
複製代碼 |
void RootMethod() { using(TransactionScope scope = new TransactionScope()) { /* Perform transactional work here */ SomeMethod(); scope.Complete(); } } void SomeMethod() { using(TransactionScope scope = new TransactionScope()) { /* Perform transactional work here */ scope.Complete(); } } |
最頂部的事務範圍稱作根範圍。
TransactionScope 類提供若干重載建構函式,這些建構函式接受 類型的枚舉,此類型定義範圍的事務性行為。
TransactionScope 對象具有三個選擇:
如果範圍是用 執行個體化的,並且存在一個環境事務,則該範圍加入該事務。在另一方面,如果沒有任何環境事務,則該範圍將建立一個新事務,並且成為根範圍。這是預設值。在使用 Required 時,無論該範圍是根範圍還是只是加入環境事務,其中的代碼都不需要在行為上有什麼不同。在這兩種情況下它的操作完全相同。
如果範圍是用 執行個體化的,則它始終是根範圍。它開始一個新事務,並且它的事務將成為該範圍內的新環境事務。
如果該範圍是用 執行個體化的,則不管是否存在環境事務,它都永遠不會參與某一事務。使用該值執行個體化的範圍始終將 null 作為其環境事務。
下表對上述選擇加以總結。
| TransactionScopeOption |
環境事務 |
該範圍參與 |
必需的 |
否 |
新事務(將成為根) |
要求建立 |
否 |
新事務(將成為根) |
抑制 |
否 |
無事務 |
必需的 |
是 |
環境事務 |
要求建立 |
是 |
新事務(將成為根) |
抑制 |
是 |
無事務 |
在 TransactionScope 對象串連現有環境事務時,處置範圍對象可能不會結束該事務,除非範圍中止該事務。如果該環境事務是由根範圍建立,則只有在處置根範圍時,才對該事務調用 Commit。如果該事務是手動建立的,則該事務的建立者中止或提交該事務後該事務結束。
下面的樣本說明一個 TransactionScope 對象,該對象建立三個嵌套的範圍對象,每個對象都用不同的 TransactionScopeOption 值執行個體化。
| C# |
複製代碼 |
using(TransactionScope scope1 = new TransactionScope()) //Default is Required { using(TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Required)) { ... } using(TransactionScope scope3 = new TransactionScope(TransactionScopeOption.RequiresNew)) { ... } using(TransactionScope scope4 = new TransactionScope(TransactionScopeOption.Suppress)) { ... } } |
該樣本說明一個不具有任何環境事務的代碼塊,它使用 Required 建立新範圍 (scope1)。範圍 scope1 是根範圍,因為它建立一個新事務(事務 A)並使事務 A 成為環境事務。Scope1 然後再建立三個對象,每個對象都具有不同的 TransactionScopeOption 值。例如,scope2 是使用 Required 建立的,並且由於有一個環境事務,因此它串連 scope1 建立的第一個事務。請注意,scope3 是新事務的根範圍,並且 scope4 不具有任何環境事務。
儘管 TransactionScopeOption 的預設值且最常用值是 Required,但其他每個值都各具不同用途。
如果您想要保留代碼部分執行的操作,並且在操作失敗的情況下不希望中止環境事務,則 Suppress 會對您很有協助。例如,在您想要執行日誌記錄或審核操作時,或者在您想要向訂戶發布事件時(不管您的環境事務是提交還是中止),上述值就很有用。該值允許您在事務範圍內具有非事務性的代碼部分,如以下樣本中所示。
| C# |
複製代碼 |
using(TransactionScope scope1 = new TransactionScope()) { try { //Start of non-transactional section using(TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Suppress)) { //Do non-transactional work here } //Restores ambient transaction here } catch {} //Rest of scope1 } |
在嵌套的範圍內投票
儘管嵌套的範圍可以加入根範圍的環境事務,但在嵌套的範圍內調用 Complete 對根範圍沒有任何影響。只有在從根範圍一直向下到最後嵌套的範圍中的所有範圍都投票決定提交該事務時,才提交該事務。
設定 TransactionScope 逾時
TransactionScope 的某些重載的建構函式接受 類型的值,該值用於控制事務的逾時。如果該逾時值設定為零,則意味著對逾時沒有限制。在以下情況下最適合採用無限制的逾時:進行調試時;您希望通過逐步驟執行代碼確定商務邏輯中的問題時;或者在嘗試確定問題時不希望所調試的事務逾時的情況。在所有其他情況下使用無限制的逾時值時應該非常謹慎,因為該值將重寫對避免發生事務死結的保護。
通常在兩種情況下將 TransactionScope 逾時設定為非預設值的其他值。第一種情況就是在開發期間,在您想要測試應用程式處理中止的事務的方式時。通過將該逾時設定為一個較小的值(例如 1 微秒),可使您的事務失敗,並可以因此觀察您的錯誤處理代碼。您將該值設定為小於預設逾時的第二種情況是,在您認為該範圍與導致死結的資源爭用有關時。在該情況中,您想要儘快中止該事務,不等待預設逾時到期。
如果某一範圍加入了環境事務,但指定了比環境事務所設定的逾時值更短的逾時值,則強制對 TransactionScope 對象採用新的、更短的逾時,並且該範圍必須在指定的嵌套時間內結束,否則將自動中止該事務。如果嵌套的範圍的逾時長於環境事務的逾時,則不會有任何影響。
設定 TransactionScope 隔離等級
TransactionScope 的某些重載的建構函式接受 類型的結構,以便除了指定逾時值之外還指定隔離等級。預設情況下,執行事務時將隔離等級設定為。選擇 Serializable 以外的隔離等級的情況最常用於需要進行大量讀取的系統。這要求使用者紮實理解交易處理理論和事務本身的語義、涉及的並發問題以及系統一致性的重要影響。
此外,並不是所有資源管理員都支援所有層級的隔離,並且它們可以選擇參與比配置層級更進階別上的事務。
除 Serializable 之外,每一隔離等級也都可能遇到因訪問相同資訊的其他事務導致的不一致。不同隔離等級之間的差別體現在讀取鎖定和寫入鎖定的用法上。只有在事務訪問資源管理員中的資料時才可能持有鎖定,或者只有在提交或中止事務前才可能持有鎖定。前者在通訊量方面表現更好,而後者在一致性上表現更出色。這兩種類型的鎖定和兩種類型的操作(讀/寫)提供了四個基本隔離等級。有關更多資訊,請參見。
在使用嵌套的 TransactionScope 對象時,必須配置所有嵌套的範圍,以便精確使用同一隔離等級(如果它們要加入環境事務)。如果某一嵌套 TransactionScope 對象嘗試加入環境事務,但它指定了不同的隔離等級,則會引發。
與 COM+ 互操作
在您建立新 TransactionScope 執行個體時,可以在某一建構函式中使用 枚舉,以便指定與 COM+ 互操作的方式。有關此內容的更多資訊,請參見與企業級服務和 COM+ 事務的互通性。