Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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
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); }
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étodoSubtract
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(String.Format("Adding {0} to {1}", n1, n2)); return n1 + n2; } [OperationBehavior(TransactionScopeRequired = true)] public double Subtract(double n1, double n2) { // Perform transactional operation RecordToLog(String.Format("Subtracting {0} from {1}", n2, 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. } }
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 obtener más información, vea Configuración de la transacción ServiceModel. Concretamente, se especifica el tipo de enlace en el atributo binding del elemento del extremo. 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 muestra 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
Para un rendimiento óptimo, debería utilizar el protocolo OleTransactions para los escenarios que implican un cliente y servicio escritos 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 los servicios WCF para aceptar ambos protocolos al proporcionar varios extremos con los enlaces adecuados específicos del protocolo, tal y como se muestra en la configuración de muestra siguiente.
<service name="CalculatorService"> <endpoint address="https://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
De forma predeterminada, las operaciones WCF completan automáticamente las transacciones si no se produce ninguna excepción no controlada. 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ónCredit1
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ónCredit2
. 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; } }
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
WCF utiliza 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(String.Format("Adding {0} to {1}", n, runningTotal)); runningTotal = runningTotal + n; return runningTotal; } [OperationBehavior(TransactionScopeRequired = true)] public double Subtract(double n) { // Perform transactional operation RecordToLog(String.Format("Subtracting {0} from {1}", n, 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.