Compartir a través de


Implementación de una transacción explícita mediante CommittableTransaction

La CommittableTransaction clase proporciona una manera explícita de que las aplicaciones usen una transacción, en lugar de usar la TransactionScope clase implícitamente. Resulta útil para las aplicaciones que quieran usar la misma transacción en varias llamadas de función o varias llamadas a subprocesos. A diferencia de la TransactionScope clase , el escritor de aplicaciones debe llamar específicamente a los Commit métodos y Rollback para confirmar o anular la transacción.

Información general de la clase CommittableTransaction

La CommittableTransaction clase deriva de la Transaction clase , por lo que proporciona toda la funcionalidad de este último. Específicamente útil es el Rollback método de la Transaction clase que también se puede usar para revertir un CommittableTransaction objeto.

La Transaction clase es similar a la CommittableTransaction clase , pero no ofrece un Commit método . Esto le permite pasar el objeto de transacción (o clones de él) a otros métodos (potencialmente en otros subprocesos) mientras sigue controlando cuándo se confirma la transacción. El código llamado puede inscribir y votar en la transacción, pero solo el creador del CommittableTransaction objeto tiene la capacidad de confirmar la transacción.

Debe tener en cuenta lo siguiente al trabajar con la clase CommittableTransaction.

  • Al crear una transacción CommittableTransaction, no se establece la transacción ambiente. Debe establecer y restablecer específicamente la transacción ambiental, para asegurarse de que los administradores de recursos operen bajo el contexto de transacción adecuado cuando sea necesario. La manera de establecer la transacción ambiental actual es estableciendo la propiedad estática Current en el objeto global Transaction .

  • No se puede reutilizar un CommittableTransaction objeto. Una vez que se ha confirmado o revertido un CommittableTransaction objeto, no se puede volver a usar 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

La creación de una instancia de CommittableTransaction no establece automáticamente el contexto de transacción ambiental. Por lo tanto, cualquier operación en un administrador de recursos no forma parte de esa transacción. La propiedad estática Current del objeto global Transaction se usa para establecer o recuperar la transacción ambiental y la aplicación debe establecerla manualmente para asegurarse de que los administradores de recursos puedan participar en la transacción. También es recomendable guardar la transacción ambiental antigua y restaurarla cuando termine de usar el CommittableTransaction objeto .

Para confirmar la transacción, debe llamar explícitamente al método Commit. Para revertir una transacción, debe llamar al Rollback método . 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 CommittableTransaction objeto se puede usar en llamadas de función y subprocesos. Sin embargo, es necesario que el desarrollador de aplicaciones controle excepciones y llame específicamente al Rollback(Exception) método en caso de errores.

Confirmación asincrónica

La CommittableTransaction clase también proporciona un mecanismo para confirmar una transacción de forma asincrónica. Una confirmación de transacción puede tardar mucho tiempo, ya que podría implicar el acceso a varias bases de datos y la posible latencia de red. Si desea evitar interbloqueos en aplicaciones de alto rendimiento, puede usar la confirmación asincrónica para finalizar el trabajo transaccional lo antes posible y ejecutar la operación de confirmación como tarea en segundo plano. Los BeginCommit métodos y EndCommit de la CommittableTransaction clase permiten hacerlo.

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 pudo confirmar por cualquier motivo, EndCommit genera 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 forma más sencilla de realizar un commit asincrónico es proporcionando un método callback, que será llamado cuando finalice el commit. Sin embargo, debe llamar al método EndCommit en el objeto original CommittableTransaction utilizado para la invocación. Para obtener ese objeto, puede aplicar una conversión descendente al parámetro IAsyncResult del método de devolución de llamada, ya que la clase CommittableTransaction implementa la clase IAsyncResult.

En el ejemplo siguiente se muestra cómo se puede realizar 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