Compartir vía


Procedimiento para crear un servicio transaccional

Este ejemplo muestra varios aspectos sobre la creación de un servicio transaccional y el uso de una transacción iniciada por el cliente para coordinar las operaciones de servicio.

Creación de un servicio transaccional

  1. Cree un contrato de servicios y anote las operaciones con el valor deseado de la enumeración TransactionFlowOption para especificar los requisitos de las transacciones entrantes. Tenga en cuenta que también puede colocar TransactionFlowAttribute en la clase de servicio que se va a implementar. Esto permite una implementación única de una interfaz para utilizar estos valores de transacción, en lugar de cada implementación.

    [ServiceContract]  
    public interface ICalculator  
    {  
        [OperationContract]  
        // Use this to require an incoming transaction  
        [TransactionFlow(TransactionFlowOption.Mandatory)]  
        double Add(double n1, double n2);  
        [OperationContract]  
        // Use this to permit an incoming transaction  
        [TransactionFlow(TransactionFlowOption.Allowed)]  
        double Subtract(double n1, double n2);  
    }  
    
  2. Cree una clase de implementación y utilice ServiceBehaviorAttribute para especificar opcionalmente TransactionIsolationLevel y TransactionTimeout. Debería tener en cuenta que, en muchos casos, el valor predeterminado de TransactionTimeout de 60 segundos y el de TransactionIsolationLevel de Unspecified son adecuados. Para cada operación, puede utilizar el atributo OperationBehaviorAttribute para especificar si el trabajo realizado dentro del método debería producirse dentro del ámbito de la transacción según el valor del atributo TransactionScopeRequired. En este caso, la transacción utilizada para el método Add es igual que la transacción entrante obligatoria que fluye desde el cliente y la transacción utilizada para el método Subtract es igual que la transacción entrante si una fluye desde el cliente, o una transacción nueva creada implícitamente y localmente.

    [ServiceBehavior(  
        TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable,  
        TransactionTimeout = "00:00:45")]  
    public class CalculatorService : ICalculator  
    {  
        [OperationBehavior(TransactionScopeRequired = true)]  
        public double Add(double n1, double n2)  
        {  
            // Perform transactional operation  
            RecordToLog($"Adding {n1} to {n2}");
            return n1 + n2;  
        }  
    
        [OperationBehavior(TransactionScopeRequired = true)]  
        public double Subtract(double n1, double n2)  
        {  
            // Perform transactional operation  
            RecordToLog($"Subtracting {n2} from {n1}");
            return n1 - n2;  
        }  
    
        private static void RecordToLog(string recordText)  
        {  
            // Database operations omitted for brevity  
            // This is where the transaction provides specific benefit  
            // - changes to the database will be committed only when  
            // the transaction completes.  
        }  
    }  
    
  3. Configure los enlaces en el archivo de configuración, especificando que debería fluir el contexto de la transacción, y los protocolos que se van a utilizar para ello. Para más información, consulte Configuración de transacciones de ServiceModel. Concretamente, se especifica el tipo de enlace en el atributo binding del elemento del punto de conexión. El elemento <endpoint> contiene un atributo bindingConfiguration que hace referencia a una configuración de enlace denominada transactionalOleTransactionsTcpBinding, tal y como se muestra en la configuración de ejemplo siguiente.

    <service name="CalculatorService">  
      <endpoint address="net.tcp://localhost:8008/CalcService"  
        binding="netTcpBinding"  
        bindingConfiguration="transactionalOleTransactionsTcpBinding"  
        contract="ICalculator"  
        name="OleTransactions_endpoint" />  
    </service>  
    

    El flujo de la transacción está habilitado en el nivel de configuración mediante el atributo transactionFlow y el protocolo de transacción se especifica mediante el atributo transactionProtocol, tal y como se muestra en la configuración siguiente.

    <bindings>  
      <netTcpBinding>  
        <binding name="transactionalOleTransactionsTcpBinding"  
          transactionFlow="true"  
          transactionProtocol="OleTransactions"/>  
      </netTcpBinding>  
    </bindings>  
    

Compatibilidad de varios protocolos de transacción

  1. Para un rendimiento óptimo, debe usar el protocolo OleTransactions en escenarios donde un cliente y un servicio se escriben con Windows Communication Foundation (WCF). Sin embargo, el protocolo WS-AtomicTransaction (WS-AT) es útil para los escenarios cuando se requiere la interoperabilidad con pilas de protocolo de otro fabricante. Puede configurar servicios WCF para aceptar ambos protocolos proporcionando varios puntos de conexión con los enlaces específicos del protocolo adecuados, tal y como se muestra en la configuración de ejemplo siguiente.

    <service name="CalculatorService">  
      <endpoint address="http://localhost:8000/CalcService"  
        binding="wsHttpBinding"  
        bindingConfiguration="transactionalWsatHttpBinding"  
        contract="ICalculator"  
        name="WSAtomicTransaction_endpoint" />  
      <endpoint address="net.tcp://localhost:8008/CalcService"  
        binding="netTcpBinding"  
        bindingConfiguration="transactionalOleTransactionsTcpBinding"  
        contract="ICalculator"  
        name="OleTransactions_endpoint" />  
    </service>  
    

    El protocolo de transacción se especifica utilizando el atributo transactionProtocol. Sin embargo, este atributo está ausente del wsHttpBinding proporcionado por sistema, ya que este enlace solo puede utilizar el protocolo WS-AT.

    <bindings>  
      <wsHttpBinding>  
        <binding name="transactionalWsatHttpBinding"  
          transactionFlow="true" />  
      </wsHttpBinding>  
      <netTcpBinding>  
        <binding name="transactionalOleTransactionsTcpBinding"  
          transactionFlow="true"  
          transactionProtocol="OleTransactions"/>  
      </netTcpBinding>  
    </bindings>  
    

Control de la realización de una transacción

  1. De forma predeterminada, las operaciones de WCF completan automáticamente las transacciones si no se producen excepciones no controladas. Puede modificar este comportamiento mediante la propiedad TransactionAutoComplete y el método SetTransactionComplete. Cuando es necesario que se produzca una operación dentro de la misma transacción como otra operación (por ejemplo, una operación de débito y crédito), puede deshabilitar el comportamiento de autocompletar definiendo la propiedad TransactionAutoComplete en false tal y como se muestra en el ejemplo de operación Debit siguiente. La transacción que la operación Debit utiliza no se completa hasta que se llama a un método con la propiedad TransactionAutoComplete definida en true, tal y como se muestra en la operación Credit1 o cuando se llama al método SetTransactionComplete para marcar explícitamente la transacción como completada, tal y como se muestra en la operación Credit2. Tenga en cuenta que las dos operaciones de crédito se muestran para fines informativos, y que una operación de crédito única sería más típica.

    [ServiceBehavior]  
    public class CalculatorService : IAccount  
    {  
        [OperationBehavior(  
            TransactionScopeRequired = true, TransactionAutoComplete = false)]  
        public void Debit(double n)  
        {  
            // Perform debit operation  
    
            return;  
        }  
    
        [OperationBehavior(  
            TransactionScopeRequired = true, TransactionAutoComplete = true)]  
        public void Credit1(double n)  
        {  
            // Perform credit operation  
    
            return;  
        }  
    
        [OperationBehavior(  
            TransactionScopeRequired = true, TransactionAutoComplete = false)]  
        public void Credit2(double n)  
        {  
            // Perform alternate credit operation  
    
            OperationContext.Current.SetTransactionComplete();  
            return;  
        }  
    }  
    
  2. Para la correlación de transacción, establecer la propiedad TransactionAutoComplete en false requiere el uso de un enlace con sesión. Este requisito se especifica con la propiedad SessionMode en ServiceContractAttribute.

    [ServiceContract(SessionMode = SessionMode.Required)]  
    public interface IAccount  
    {  
        [OperationContract]  
        [TransactionFlow(TransactionFlowOption.Allowed)]  
        void Debit(double n);  
        [OperationContract]  
        [TransactionFlow(TransactionFlowOption.Allowed)]  
        void Credit1(double n);  
        [OperationContract]  
        [TransactionFlow(TransactionFlowOption.Allowed)]  
        void Credit2(double n);  
    }  
    

Control de la duración de una instancia de servicio transaccional

  1. WCF usa la propiedad ReleaseServiceInstanceOnTransactionComplete para especificar si se libera la instancia del servicio subyacente cuando se completa una transacción. Puesto que el valor predeterminado es true, a menos que se configure lo contrario, WCF muestra un comportamiento de activación "Just-In-Time" eficaz y predecible. Las llamadas a un servicio en una transacción subsiguiente aseguran una nueva instancia del servicio sin restos del estado de la transacción anterior. Aunque esto resulta a menudo útil, a veces puede querer mantener el estado dentro de la instancia del servicio más allá de la realización de la transacción. Algunos ejemplos de ello serían cuándo resulta costoso recuperar o volver a constituir el estado necesario o los controladores a los recursos. Puede hacer esto al definir la propiedad ReleaseServiceInstanceOnTransactionComplete en false. Con ese valor, la instancia y cualquier estado asociado estarán disponibles en llamadas subsiguientes. Al utilizar esto, proporcione la consideración adecuada a cuándo y cómo se borrarán y completarán las transacciones y el estado. El ejemplo siguiente muestra cómo hacer esto manteniendo la instancia con la variable runningTotal.

    [ServiceBehavior(TransactionIsolationLevel = [ServiceBehavior(  
        ReleaseServiceInstanceOnTransactionComplete = false)]  
    public class CalculatorService : ICalculator  
    {  
        double runningTotal = 0;  
    
        [OperationBehavior(TransactionScopeRequired = true)]  
        public double Add(double n)  
        {  
            // Perform transactional operation  
            RecordToLog($"Adding {n} to {runningTotal}");
            runningTotal = runningTotal + n;  
            return runningTotal;  
        }  
    
        [OperationBehavior(TransactionScopeRequired = true)]  
        public double Subtract(double n)  
        {  
            // Perform transactional operation  
            RecordToLog($"Subtracting {n} from {runningTotal}");
            runningTotal = runningTotal - n;  
            return runningTotal;  
        }  
    
        private static void RecordToLog(string recordText)  
        {  
            // Database operations omitted for brevity  
        }  
    }  
    

    Nota

    Desde que la duración de la instancia es un comportamiento que es interno al servicio y controlado a través de la propiedad ServiceBehaviorAttribute, no se requiere ninguna modificación en la configuración de servicio ni en el contrato de servicio para establecer el comportamiento de la instancia. Además, la conexión no contendrá ninguna representación de esto.