使用 CommittableTransaction 执行显式事务

CommittableTransaction 类为应用程序提供使用事务的显式方法,而不是隐式使用 TransactionScope 类。 它适用于希望跨多个函数调用或多个线程调用使用相同的事务的应用程序。 与类 TransactionScope 不同,应用程序编写器需要专门调用 CommitRollback 方法才能提交或中止事务。

CommittableTransaction 类概述

CommittableTransaction 派生自 Transaction 该类,因此提供后者的所有功能。 特别有用的是Rollback类中的Transaction方法,它也可以用于回滚CommittableTransaction对象。

Transaction 类类似于 CommittableTransaction 类,但不提供 Commit 方法。 这样,便可以将事务对象(或它的克隆)传递给其他方法(可能在其他线程上),同时仍控制何时提交事务。 被调用的代码可以在事务中登记并为其投票,但只有 CommittableTransaction 对象的创建者才能提交事务。

使用CommittableTransaction类时,您应注意以下事项:

  • 创建 CommittableTransaction 事务并不会设置环境事务。 需要专门设置和重置环境事务,以确保资源管理器在适当的事务上下文下正常运行。 设置当前环境事务的方法是在全局Current对象上设置静态Transaction属性。

  • 对象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 类还提供异步提交事务的机制。 事务提交可能需要很长时间,因为它可能涉及多个数据库访问和可能的网络延迟。 若要避免高吞吐量应用程序中的死锁,可以使用异步提交尽快完成事务工作,并将提交作作为后台任务运行。 类BeginCommitEndCommitCommittableTransaction方法允许你这样做。

调用 BeginCommit 可将提交延迟调度到线程池中的某一线程。 此外,还可以调用 EndCommit 来确定是否确实已提交事务。 如果事务因任何原因未能提交, EndCommit 则引发事务异常。 如果在调用 EndCommit 时仍未提交事务,则调用方就会处于锁定状态,直到提交或中止事务为止。

执行异步提交的最简单方法是提供回调方法,在完成提交时调用。 但是,必须在用于调用调用的原始EndCommit对象上调用CommittableTransaction该方法。 若要获取该对象,可以向下转换回调方法的 IAsyncResult 参数,因为 CommittableTransaction 类实现 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  
     }  
}  

另请参阅