Compartir a través de


Implementar una transacción explícita mediante una transacción confirmable

La clase CommittableTransaction proporciona a las aplicaciones una manera explícita de utilizar una transacción, a diferencia de utilizar implícitamente la clase TransactionScope. Es útil para las aplicaciones que desean utilizar la misma transacción por varias llamadas de función o llamadas de subproceso múltiples. A diferencia de la clase TransactionScope, el sistema de escritura de la aplicación ha de llamar específicamente a los métodos Commit y Rollback para confirmar o anular la transacción.

Información general sobre la clase CommittableTransaction.

La clase CommittableTransaction deriva de la clase Transaction, proporcionando, por consiguiente, toda la funcionalidad del último. Específicamente útil es el método Rollback en la clase Transaction que también se puede utilizar para revertir un objeto CommittableTransaction.

La clase Transaction es similar a la clase CommittableTransaction pero no proporciona un método Commit. Esto le permite pasar el objeto de transacción (o clones de él) a otros métodos (potencialmente en otros subprocesos) mientras todavía se controla cuando se confirma la transacción. El código llamado puede dar de alta y votar en la transacción, pero solo el creador del objeto CommittableTransaction tiene la capacidad de confirmar la transacción.

Debe tener en cuenta lo siguiente cuando trabaje con la clase CommittableTransaction,

  • Al crear una transacción CommittableTransaction, no se establece la transacción ambiente. Tendrá que establecer y restablecer específicamente la transacción de ambiente con el fin de garantizar que los administradores de recursos funcionen bajo el contexto de transacción correcto cuando sea apropiado. La manera de establecer la transacción ambiente actual está estableciendo la propiedad Current estática en el objeto Transaction global.

  • No se puede reutilizar un objeto CommittableTransaction. Una vez confirmado o revertido un objeto CommittableTransaction, no se puede utilizar de nuevo en una transacción. Es decir, no se puede establecer como el contexto de la transacción ambiente actual.

Crear un CommittableTransaction

En el ejemplo siguiente se crea una nueva instancia de CommittableTransaction y se confirma.

//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

Al crear una instancia de CommittableTransaction, no se establece automáticamente el contexto de la transacción ambiente. Por consiguiente, cualquier operación en un administrador de recursos no forma parte de esa transacción. La propiedad Current estática en el objeto Transaction global se utiliza para establecer o recuperar la transacción ambiente y la aplicación debe establecerlo manualmente para asegurarse de que los administradores de recursos pueden participar en la transacción. También es una práctica buena guardar la transacción ambiente anterior y restaurarla al terminar de utilizar el objeto CommittableTransaction.

Para confirmar la transacción se necesita llamar explícitamente al método Commit . Para deshacer una transacción, debería llamar al método Rollback. Es importante tener en cuenta que hasta que CommittableTransaction se haya confirmado o revertido, se bloquean todos los recursos implicados todavía en esa transacción.

Un objeto CommittableTransaction se puede utilizar por las llamadas de función y subprocesos. Sin embargo, es tarea del desarrollador de aplicaciones administrar las excepciones y específicamente llamar al método Rollback(Exception) en caso de error.

Confirmación asincrónica

La clase CommittableTransaction también proporciona un mecanismo para confirmar de forma asincrónica una transacción. Una confirmación de la transacción puede tardar un tiempo sustancial, al igual que podría implicar varios accesos a bases de datos y una posible latencia de red. Al desear evitar los interbloqueos en aplicaciones de alto rendimiento, puede utilizar la confirmación asincrónica para finalizar lo antes posible el trabajo transaccional y ejecutar la operación de la confirmación como una tarea en segundo plano. BeginCommit y los métodos EndCommit de la clase CommittableTransaction le permiten esto.

Puede llamar BeginCommit para enviar el retraso de la confirmación a un subproceso del grupo de subprocesos. También puede llamar EndCommit para determinar si se ha confirmado la transacción realmente. Si la transacción no se confirma por cualquier razón, EndCommit se producirá una excepción de transacción. Si no se confirma la transacción todavía cuando se llama EndCommit, se bloquea el llamador hasta que la transacción se confirme o anule.

La manera más fácil de hacer una confirmación asincrónica es proporcionando un método de devolución de llamada, para ser llamado cuando la confirmación esté terminada. Sin embargo, debe llamar al método EndCommit en el objeto CommittableTransaction original utilizado para invocar la llamada. Para obtener ese objeto, se puede inclinar hacia abajo el parámetro IAsyncResult del método de devolución de llamada, desde que la clase CommittableTransaction implementa IAsyncResult.

El ejemplo siguiente muestra cómo se puede hacer una confirmación asincrónica.

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  
     }  
}  

Consulte también