Freigeben über


WCF y Transacciones

La orientación a servicios nos facilita el desarrollo de sistemas distribuidos o no que se apoyan en procesos de negocio facilitando la tarea de encajar dichos procesos dentro de nuestro sistema. A medida que han ido evolucionando las soluciones han aparecido nuevas necesidades como Seguridad, Transaccionalidad, etc y que son viejas conocidas en ciertas tecnologías de desarrollo distribuido (DCOM/COM+, RPC, etc) pero en otros casos se han desarrollado nuevos estandares como en el caso de WebServices con las tecnologías WS-* que se pueden utilizar desde WSE y WCF. Algunas de estos estandares son:

  • WS-Security que define el esquema para el paso de credenciales con encriptación basado en modelos de certificados, modelos propios, etc.
  • WS-Coordinator que determina como notificar acciones entre n servicios
  • WS-Atomic Transaction como estandar para realizar transacciones atómicas, etc

En este post me voy a centrar en la parte de WS-Transaction aunque está muy ligada con WS-Coordination y WS-Security (una transacción requiere seguridad y coordinación) que trae implementada WCF.

Windows Communication Foundation tiene como característica principal aislarnos del canal de comunicaciones a la hora de crear un servicio y por lo tanto es el propio canal el que proporciona o no la posibilidad del uso de transacciones (esto es importante, puesto que aunque definamos que un servicio es Transaccional el comportamiento no es proporcionado por todos los canales de comunicaciones). En este escenario, la forma de gestionar Transacciones a través de MSMQ y la de WebServices son completamente distintas.

Centrandonos en el modelo Transaccional de WCF, nosotros podemos definir un servicio transaccional en el que cada llamada puede estar incluida dentro de la transacción. Si seguimos el modelo de WCF las transacciones dependen del código de servicio siendo necesario cerrar las Transacciones desde el propio servicio. En este escenario, necesitaremos establecer session entre las llamadas al servicio.

[ServiceBehavior(TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable)]
public class CalculatorService: ICalculatorLog
{
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
    public string CreateRequest(string productId,  string description,  int numberOfItems)
    {
           ...... Database code ....

    }

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
    public double CompleteRequest(string requestId)
    {
          ....Database code...

          OperationContext.Current.SetComplete ();

    }
}

Este modelo, siendo muy practico para servicios internos, no es el método más flexible y más escalabe pusto que nos requiere el uso de Sesion y la responsabilidad de cerrar la Transacción queda delegada en el orden de llamadas al servicío.

El modelo de WS-AT cambia el punto de responsabilidad de la transacción desde el servidor al cliente. En este modelo, todos los participantes en la transacción (el cliente y los servicios) son recursos del coordinador transaccional. Las llamadas a los servicios envían como parte del mensaje (en las cabeceras) el identificador de Transacción apoyandose en el protocolo WS-Coordination. De esta manera las llamdas estan coordinandose dentro de un contexto transaccional que está en espera de que le confirmen la acción final. La pieza clave de todo este modelo es el Coordinador de Transacciones, tarea que realiza el DTC (Distributed Transaction Coordinator) que actualmente soporta el protocolo OleTransactions y WS-Atomic Transactions (actualizando y configurando el sistema como se indica en WS-AtomicTransaction Configuration Utility (wsatConfig.exe) e instalando este hotfix Update for Windows Communication Foundation (KB912817)).

Cuando una transacción se inicia en cliente bajo WS-AT, es promovida hasta el DTC local que a partir de este momento es el encargado de gestionar el Commit o el Rollback. Cuando hemos terminado de realizar una operativa y notificamos el Commit o el Rollback, es el DTC el encargado de notificar (siguiendo un protocolo two phase commit de aviso y confirmación) a todos los Coordinadores remotos involucrados como debe quedar la transacción. La secuencia se ejecuta de esta manera:

  • Las llamadas a servicio se coordinan compartiendo el Identificador de Transacción.
  • Cuando hacemos Commit, el sistema notifica que se desea realizar dicha acción. El DTC notificará a los Coordinadores remotos y espera que le confirmen que están listos para hacer Commit.
  • Si uno de los involucrados no responde afirmativamente, entonces se notifica a todos los recursos que hagan Rollback (los coordinadores garantizan que se realizará). En caso contrario se notifica que hagan commit.

El protocolo que se utiliza para coordinar toda esta operativa es el WS-AT y wl WS-Coord, y opcionalmente por seguridad se usa WS-Security. Al final, desde un punto de vista practico es como llamar a un WebService con una interfaz y un esquema concreto.

WCF nos facilita toda esta operativa haciendola transparente a nosotros, gestionando el DTC e integrandose con TransactionServices (y por compatibilidad con COM+). Nuestro código de servicio se modifica ligeramente:

[ServiceBehavior(TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable)]
public class CalculatorService: ICalculatorLog
{
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
    public string CreateRequest(string productId,  string description,  int numberOfItems)
    {
           ...... Database code ....

    }

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
    public double CompleteRequest(string requestId)
    {
          ....Database code...

          OperationContext.Current.SetComplete ();// Ya no será necesario

    }
}

En ambos casos nuestro código de cliente es muy sencillo, limitandose a:

using (TransactionScope txScope = new TransactionScope ())

{

           CalculatorServiceProxy proxy = new CalculatorServiceProxy() ;

           string requestId = proxy.CreateRequest ("09900", "unused products", 10);

           proxy.CompleteRequest (requestId);

}

Lo mejor de WCF, es que podemos decidir que configuración aplicar por código y por fichero de configuración (pudiendo configurar canal de comunicaciones, transaccionalidad, sesion, seguridad, etc). A continuación os incluyo ejemplos de configuración y de código para activar los dos modelos Transaccionales:

WCF Transactions Configuration Sample

  <bindings>

    <wsHttpBinding>

      <binding name="MyCustomBinding"

               transactionFlow="true" />

    </wsHttpBinding >

  </bindings>

WCF Transactions Code Sample

WsHttpBinding binding = new WsHttpBinding ();

binding.TransactionFlow = true;

WCF WS-AT Configuration Sample

<bindings>

    <netTcpBinding>

      <binding name="MyCustomWSAtBinding"

               transactionFlow="true"

               transactionProtocol="WSAtomicTransactionOctober2004" />

    </netTcpBinding>

  </bindings>

 

WCF WS-AT Code Sample

 

WsHttpBinding binding = new WsHttpBinding ();

binding.TransactionFlow = true;

binding.TransactionProtocol = TransactionProtocol.WSAtomicTransactionOctober2004;

 

Espero haber aclarado un poco la programación de WCF haciendo uso de Transacciones, pero por si acaso necesitais más información os incluyo algunos enlaces de interes:

 

Aprende el ABC de la programación de Windows Communication Foundation

 

Transactions in WCF and .NET

Comments