Flujo de las transacciones en los servicios de flujo de trabajo
Los servicios y clientes de flujo de trabajo pueden participar en las transacciones. Para que una operación de servicio se convierta en parte de una transacción de ambiente, coloque una actividad de Receive dentro de una actividad de TransactedReceiveScope. En cualquier llamada realizada por un objeto Send o una actividad de SendReply dentro de TransactedReceiveScope también se realizará dentro de la transacción de ambiente. Una aplicación cliente del flujo de trabajo puede crear una transacción de ambiente utilizando la actividad de TransactionScope y operaciones de servicio de llamada que usen la transacción de ambiente. Este tema sirve de guía para crear un servicio de flujo de trabajo y un cliente de flujo de trabajo que participan en transacciones.
Advertencia
Si una instancia del servicio de flujo de trabajo se carga dentro de una transacción y el flujo de trabajo contiene una actividad Persist, la instancia de flujo de trabajo se bloqueará hasta que se agote el tiempo de espera de la transacción.
Importante
Siempre que use un objeto TransactedReceiveScope se recomienda que coloque todas las recepciones en el flujo de trabajo dentro de actividades TransactedReceiveScope.
Importante
Cuando se usa el objeto TransactedReceiveScope y los mensajes llegan en el orden incorrecto, el flujo de trabajo se anulará al intentar entregar el primer mensaje que lo indique. Debe asegurarse de que el flujo de trabajo siempre está en un punto de detención coherente cuando el flujo de trabajo está inactivo. De este modo, podrá reiniciar el flujo de trabajo a partir de un punto de persistencia anterior si el flujo de trabajo se anula.
Crear una biblioteca compartida
Cree una nueva solución de Visual Studio vacía.
Agregue un nuevo proyecto de biblioteca de clases denominado
Common
. Agregue referencias a los siguientes ensamblados:System.Activities.dll
System.ServiceModel.dll
System.ServiceModel.Activities.dll
System.Transactions.dll
Agregue una nueva clase denominada
PrintTransactionInfo
al proyectoCommon
. Esta clase se deriva de NativeActivity y sobrecarga el método Execute.using System; using System; using System.Activities; using System.Transactions; namespace Common { public class PrintTransactionInfo : NativeActivity { protected override void Execute(NativeActivityContext context) { RuntimeTransactionHandle rth = context.Properties.Find(typeof(RuntimeTransactionHandle).FullName) as RuntimeTransactionHandle; if (rth == null) { Console.WriteLine("There is no ambient RuntimeTransactionHandle"); } Transaction t = rth.GetCurrentTransaction(context); if (t == null) { Console.WriteLine("There is no ambient transaction"); } else { Console.WriteLine("Transaction: {0} is {1}", t.TransactionInformation.DistributedIdentifier, t.TransactionInformation.Status); } } } }
Esta es una actividad nativa que muestra información acerca de la transacción de ambiente y se utiliza en ambos flujos de trabajo, de cliente y de servicio, que se usan en este tema. Compile la solución para hacer que esta actividad esté disponible en la sección Común del Cuadro de herramientas.
Implementar el servicio de flujo de trabajo
Agregue un nuevo Servicio de flujo de trabajo de WCF, denominado
WorkflowService
, al proyectoCommon
. Para ello, haga clic con el botón secundario en el proyectoCommon
, seleccione Agregar, Nuevo elemento..., seleccione Flujo de trabajo en Plantillas instaladas y luego Servicio de flujo de trabajo de WCF.Elimine las actividades
ReceiveRequest
ySendResponse
predeterminadas.Arrastre y coloque una actividad de WriteLine en la actividad de
Sequential Service
. Establezca la propiedad de texto en"Workflow Service starting ..."
como se muestra en el siguiente ejemplo.![Incorporación de una actividad WriteLine a la actividad de servicio secuencial(./media/flow-transactions-into-and-out-of-workflow-services/add-writeline-sequential-service.jpg)
Arrastre y coloque una actividad TransactedReceiveScope después de la actividad de WriteLine. La actividad TransactedReceiveScope se puede encontrar en la sección Mensajería del Cuadro de herramientas. La actividad TransactedReceiveScope está compuesta por dos secciones Solicitud y Cuerpo. La sección Solicitud contiene la actividad Receive. La sección Cuerpo contiene las actividades que se ejecutan dentro de una transacción una vez recibido un mensaje.
Seleccione la actividad TransactedReceiveScope y haga clic en el botón Variables. Agregue las variables siguientes.
Nota:
Puede eliminar la variable de datos que está allí de forma predeterminada. También puede utilizar la variable de controlador existente.
Arrastre y coloque una actividad Receive dentro de la sección Solicitud de la actividad TransactedReceiveScope. Establezca las siguientes propiedades:
Property Value CanCreateInstance True (activar la casilla) OperationName StartSample ServiceContractName ITransactionSample El flujo de trabajo debería ser similar a este:
Haga clic en el vínculo Definir... en la actividad Receive y realice la siguiente configuración:
Arrastre y coloque una actividad de Sequence en la sección Cuerpo de TransactedReceiveScope. Dentro de la actividad de Sequence, arrastre las dos actividades de WriteLine y establezca las propiedades Text como se muestra en la siguiente tabla.
Actividad Valor Primera propiedad WriteLine "Servicio: recepción completada" Segunda propiedad WriteLine "Servicio: recibido = " + requestMessage El flujo de trabajo ahora debería ser similar a este:
Arrastre y coloque la actividad
PrintTransactionInfo
después de la segunda actividad WriteLine en la sección Cuerpo de la actividad TransactedReceiveScope.Arrastre y coloque una actividad de Assign después de la actividad de
PrintTransactionInfo
y establezca sus propiedades según la siguiente tabla.Propiedad Valor En replyMessage Valor "Servicio: enviando respuesta." Arrastre y coloque una actividad de WriteLine después de la actividad de Assign y establezca su propiedad Text en "Servicio: iniciar respuesta".
El flujo de trabajo ahora debería ser similar a este:
Haga clic con el botón secundario en la actividad Receive, seleccione Crear SendReply y péguela después de la última actividad WriteLine. Haga clic en el vínculo Definir... en la actividad
SendReplyToReceive
y realice la siguiente configuración.Arrastre y coloque una actividad WriteLine después de la actividad
SendReplyToReceive
y establezca su propiedad Text en "Servicio: respuesta enviada".Arrastre y coloque una actividad de WriteLine en la parte inferior del flujo de trabajo y establezca su propiedad Text en "Servicio: el flujo de trabajo finaliza, presione Entrar para salir".
El flujo de trabajo del servicio completado debería ser similar a:
Implementar el cliente de flujo de trabajo
Agregue una nueva aplicación Flujo de trabajo WCF, denominada
WorkflowClient
, al proyectoCommon
. Para ello, haga clic con el botón secundario en el proyectoCommon
, seleccione Agregar, Nuevo elemento..., seleccione Flujo de trabajo en Plantillas instaladas y seleccione Actividad.Arrastre y coloque una actividad Sequence en la superficie de diseño.
Dentro de la actividad Sequence, arrastre y coloque una actividad de WriteLine y establezca su propiedad Text en
"Client: Workflow starting"
. El flujo de trabajo ahora debería ser similar a este:Arrastre y coloque una actividad de TransactionScope después de la actividad de WriteLine. Seleccione la actividad de TransactionScope, haga clic en el botón Variables y agregue las siguientes variables.
Arrastre y coloque una actividad de Sequence en el cuerpo de la actividad de TransactionScope.
Arrastre y coloque una actividad de
PrintTransactionInfo
dentro de Sequence.Arrastre y coloque una actividad WriteLine después de la actividad
PrintTransactionInfo
y establezca su propiedad Text en "Cliente: comenzando envío". El flujo de trabajo ahora debería ser similar a este:Arrastre y coloque una actividad de Send después de la actividad de Assign y establezca las siguientes propiedades:
Propiedad Valor EndpointConfigurationName workflowServiceEndpoint OperationName StartSample ServiceContractName ITransactionSample El flujo de trabajo ahora debería ser similar a este:
Haga clic en el vínculo Definir... y realice la siguiente configuración:
Haga clic con el botón secundario en la actividad Send y seleccione Crear ReceiveReply. La actividad de ReceiveReply se colocará automáticamente después de la actividad de Send.
Haga clic en el vínculo Definir... en la actividad ReceiveReplyForSend y realice la siguiente configuración:
Arrastre y coloque una actividad de WriteLine entre las actividades de Send y ReceiveReply, y establezca su propiedad Text en "Cliente: envío completado".
Arrastre y coloque una actividad de WriteLine después de la actividad de ReceiveReply y establezca su propiedad Text en "Cliente: respuesta recibida = " + replyMessage
Arrastre y coloque una actividad de
PrintTransactionInfo
después de la actividad de WriteLine.Arrastre y coloque una actividad WriteLine al final del flujo de trabajo y establezca su propiedad Text en "Finaliza el flujo de trabajo del cliente". El flujo de trabajo del cliente completado debe ser similar al diagrama siguiente.
Compile la solución.
Crear la aplicación de servicio
Agregue un nuevo proyecto Aplicación de consola denominado
Service
a la solución. Agregue referencias a los siguientes ensamblados:System.Activities.dll
System.ServiceModel.dll
System.ServiceModel.Activities.dll
Abra el archivo Program.cs generado y el siguiente código:
static void Main() { Console.WriteLine("Building the server."); using (WorkflowServiceHost host = new WorkflowServiceHost(new DeclarativeServiceWorkflow(), new Uri("net.tcp://localhost:8000/TransactedReceiveService/Declarative"))) { //Start the server host.Open(); Console.WriteLine("Service started."); Console.WriteLine(); Console.ReadLine(); //Shutdown host.Close(); }; }
Agregue el archivo app.config siguiente al proyecto.
<?xml version="1.0" encoding="utf-8" ?> <!-- Copyright © Microsoft Corporation. All rights reserved. --> <configuration> <system.serviceModel> <bindings> <netTcpBinding> <binding transactionFlow="true" /> </netTcpBinding> </bindings> </system.serviceModel> </configuration>
Creación de la aplicación cliente
Agregue un nuevo proyecto Aplicación de consola denominado
Client
a la solución. Agregue una referencia a System.Activities.dll.Abra el archivo Program.cs y agregue el siguiente código.
class Program { private static AutoResetEvent syncEvent = new AutoResetEvent(false); static void Main(string[] args) { //Build client Console.WriteLine("Building the client."); WorkflowApplication client = new WorkflowApplication(new DeclarativeClientWorkflow()); client.Completed = Program.Completed; client.Aborted = Program.Aborted; client.OnUnhandledException = Program.OnUnhandledException; //Wait for service to start Console.WriteLine("Press ENTER once service is started."); Console.ReadLine(); //Start the client Console.WriteLine("Starting the client."); client.Run(); syncEvent.WaitOne(); //Sample complete Console.WriteLine(); Console.WriteLine("Client complete. Press ENTER to exit."); Console.ReadLine(); } private static void Completed(WorkflowApplicationCompletedEventArgs e) { Program.syncEvent.Set(); } private static void Aborted(WorkflowApplicationAbortedEventArgs e) { Console.WriteLine("Client Aborted: {0}", e.Reason); Program.syncEvent.Set(); } private static UnhandledExceptionAction OnUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e) { Console.WriteLine("Client had an unhandled exception: {0}", e.UnhandledException); return UnhandledExceptionAction.Cancel; } }