使用 CommittableTransaction 實作明確交易

CommittableTransaction 類別為應用程式提供使用交易的明確方式,而非隱含地使用 TransactionScope 類別。 這個方法對想要跨多個函式呼叫或多個執行緒呼叫來使用相同交易的應用程式也很有用處。 與 TransactionScope 類別不同的是,應用程式寫入器需要特別呼叫 CommitRollback 方法,才能認可或中止交易。

CommittableTransaction 類別的概觀

CommittableTransaction 類別係衍生自 Transaction 類別,因此可提供後者所有功能。 Rollback 類別上的 Transaction 方法特別有用,因為它同時能用來復原 CommittableTransaction 物件。

Transaction 類別與 CommittableTransaction 類別類似,但是不會提供 Commit 方法。 它能讓您在控制交易認可時間的同時,將交易物件 (或其複製品) 傳遞給其他方法 (可能透過其他執行緒)。 呼叫的程式碼可以登記並投票給交易,但是只有 CommittableTransaction 物件的建立者有能力認可交易。

使用 CommittableTransaction 類別時,您應該注意下列事項。

  • 建立 CommittableTransaction 交易不會設定環境交易。 您需要特別設定及重設環境交易,以確保資源管理員在適當時機,於正確的交易內容下作業。 您可以在全域 Current 物件上設定靜態 Transaction 屬性,來設定目前的環境交易。

  • 您無法重複使用 CommittableTransaction 物件, 一旦 CommittableTransaction 物件已經認可或復原,將無法重複使用在交易中。 亦即,您無法將其設為目前的環境交易內容。

建立 CommittableTransaction

下列範例會建立新的 CommittableTransaction 並加以認可。

//Create a committable transaction
tx = new CommittableTransaction();

SqlConnection myConnection = new SqlConnection("server=(local)\\SQLExpress;Integrated Security=SSPI;database=northwind");
SqlCommand myCommand = new SqlCommand();

//Open the SQL connection
myConnection.Open();

//Give the transaction to SQL to enlist with
myConnection.EnlistTransaction(tx);

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

// Commit or rollback the transaction
while (true)
{
    Console.Write("Commit or Rollback? [C|R] ");
    ConsoleKeyInfo c = Console.ReadKey();
    Console.WriteLine();

    if ((c.KeyChar == 'C') || (c.KeyChar == 'c'))
    {
        tx.Commit();
        break;
    }
    else if ((c.KeyChar == 'R') || (c.KeyChar == 'r'))
    {
        tx.Rollback();
        break;
    }
}
myConnection.Close();
tx = null;
tx = New CommittableTransaction

Dim myConnection As New SqlConnection("server=(local)\SQLExpress;Integrated Security=SSPI;database=northwind")
Dim myCommand As New SqlCommand()

'Open the SQL connection
myConnection.Open()

'Give the transaction to SQL to enlist with
myConnection.EnlistTransaction(tx)

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

'Commit or rollback the transaction
Dim c As ConsoleKeyInfo
While (True)
    Console.Write("Commit or Rollback? [C|R] ")
    c = Console.ReadKey()
    Console.WriteLine()

    If (c.KeyChar = "C") Or (c.KeyChar = "c") Then
        tx.Commit()
        Exit While
    ElseIf ((c.KeyChar = "R") Or (c.KeyChar = "r")) Then
        tx.Rollback()
        Exit While
    End If
End While

myConnection.Close()
tx = Nothing

建立 CommittableTransaction 執行個體不會自動設定環境交易內容。 因此,資源管理員上的任何作業都不是該交易的一部分。 全域 Current 物件上的靜態 Transaction 屬性是用來設定或擷取環境交易,應用程式必須手動加以設定以確保資源管理員可以參與交易。 儲存舊有的環境交易並在 CommittableTransaction 物件使用完畢時將其還原是很好的使用習慣。

為了認可交易,您需要明確地呼叫 Commit 方法。 若要復原交易,您應該呼叫 Rollback 方法。 在已認可或復原 CommittableTransaction 之前,所有與該交易有關的資源都會保持鎖定,這點請您特別注意。

CommittableTransaction 物件可以供多個函式呼叫與執行緒使用。 然而,應用程式開發人員有權決定是否要在發生失敗時,處理例外狀況並特別呼叫 Rollback(Exception) 方法。

非同步認可

CommittableTransaction 類別同時提供您以非同步方式認可交易的機制。 交易認可作業會花掉很多時間,因為此作業牽涉到多個資料庫存取,而且可能會讓網路產生延遲現象。 當您想要避免高輸送量應用程式中出現死結 (Deadlock) 的情況時,可以使用非同步認可方式來儘速完成交易作業,並以背景工作方式來執行認可作業。 BeginCommit 類別的 EndCommitCommittableTransaction 方法可讓您這麼做。

您可以呼叫 BeginCommit,將認可停頓分配給來自執行緒集區的執行緒。 您也可以呼叫 EndCommit 來判斷交易是否已實際認可。 如果交易因為某種原因而失敗,則 EndCommit 會引發交易例外狀況。 如果交易在呼叫 EndCommit 時尚未認可,則呼叫端會在交易認可或中止之前一直保持封鎖狀態。

進行非同步認可的最簡便方式,就是提供可在完成認可時加以呼叫的回呼方法。 但是,您必須在用於叫用呼叫的原始 EndCommit 物件上呼叫 CommittableTransaction 方法。 若要取得該物件,由於 CommittableTransaction 類別會實作 IAsyncResult 類別,因此您可以向下轉換回撥方法的 IAsyncResult 參數。

下列範例示範如何完成非同步認可。

public void DoTransactionalWork()  
{  
     Transaction oldAmbient = Transaction.Current;  
     CommittableTransaction committableTransaction = new CommittableTransaction();  
     Transaction.Current = committableTransaction;  
  
     try  
     {  
          /* Perform transactional work here */  
          // No errors - commit transaction asynchronously  
          committableTransaction.BeginCommit(OnCommitted,null);  
     }  
     finally  
     {  
          //Restore the ambient transaction
          Transaction.Current = oldAmbient;  
     }  
}  
void OnCommitted(IAsyncResult asyncResult)  
{  
     CommittableTransaction committableTransaction;  
     committableTransaction = asyncResult as CommittableTransaction;
     Debug.Assert(committableTransaction != null);  
     try  
     {  
          using(committableTransaction)  
          {  
               committableTransaction.EndCommit(asyncResult);  
          }  
     }  
     catch(TransactionException e)  
     {  
          //Handle the failure to commit  
     }  
}  

另請參閱