Flux de transactions vers et depuis des services de workflow
Les services et clients de workflow peuvent participer aux transactions. Pour qu'une opération de service fasse partie d'une transaction ambiante, placez une activité Receive dans une activité TransactedReceiveScope. Tous les appels effectués par une activité Send ou SendReply au sein de l’activité TransactedReceiveScope seront également effectués dans la transaction ambiante. Une application cliente de workflow peut créer une transaction ambiante en utilisant l’activité TransactionScope et appeler des opérations de service à l’aide de la transaction ambiante. Cette rubrique vous guide dans la création d’un service de workflow et d’un client de workflow qui participent à des transactions.
Avertissement
Si une instance de service de workflow est chargée dans une transaction et que le workflow contient une activité Persist, l’instance de workflow est bloquée le temps que la transaction expire.
Important
Lorsque vous utilisez une activité TransactedReceiveScope, il est recommandé de placer toutes les réceptions dans le workflow dans les activités TransactedReceiveScope.
Important
Lorsque vous utilisez TransactedReceiveScope et les messages arrivent dans le mauvais ordre incorrect, le workflow est abandonné lorsque vous tentez de livrer le premier message dans le désordre. Vous devez vous assurer que votre workflow est toujours à un point d'arrêt cohérent lorsqu'il est inactif. Cela vous permet de redémarrer le workflow à partir d'un point de persistance précédent s'il est abandonné.
Créer une bibliothèque partagée
Créez une solution Visual Studio vide.
Ajoutez un nouveau projet de bibliothèque de classes nommé
Common
. Ajoutez des références aux assemblys suivants :System.Activities.dll
System.ServiceModel.dll
System.ServiceModel.Activities.dll
System.Transactions.dll
Ajoutez une nouvelle classe nommée
PrintTransactionInfo
au projetCommon
. Cette classe est dérivée de NativeActivity et surcharge la méthode 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); } } } }
Il s’agit d’une activité native qui affiche des informations sur la transaction ambiante et est utilisée dans les workflows du service et du client utilisés dans cette rubrique. Générez la solution pour rendre cette activité disponible dans la section Commun de la boîte à outils.
Implémenter le service de workflow
Ajoutez un nouveau service de workflow WCF, nommé
WorkflowService
, au projetCommon
. Pour ce faire, cliquez avec le bouton droit sur le projetCommon
, sélectionnez Ajouter, Nouvel élément..., puis Workflow sous Modèles installés et enfin Service de workflow WCF.Supprimez les activités par défaut
ReceiveRequest
etSendResponse
.Faites glisser et déposez une activité WriteLine dans l’activité
Sequential Service
. Affectez à la propriété Text la valeur"Workflow Service starting ..."
, comme le montre l'exemple suivant.![Ajout d’une activité WriteLine à l’activité de service séquentiel(./media/flowing-transactions-into-and-out-of-workflow-services/add-writeline-séquentiel-service.jpg)
Faites glisser et déposez une activité TransactedReceiveScope après l’activité WriteLine. L’activité TransactedReceiveScope se trouve dans la section Messagerie de la Boîte à outils. L’activité TransactedReceiveScope est composée de deux sections : Requête et Corps. La section Requête contient l’activité Receive. La section Corps contient les activités à exécuter dans une transaction après réception d’un message.
Sélectionnez l’activité TransactedReceiveScope et cliquez sur le bouton Variables. Ajoutez les variables suivantes.
Remarque
Vous pouvez supprimer la variable de données proposée à cet endroit par défaut. Vous pouvez également utiliser la variable de handle existante.
Faites glisser et déposez une activité Receive dans la section Requête de l’activité TransactedReceiveScope. Définissez les propriétés suivantes :
Property Valeur CanCreateInstance True (activez la case à cocher) NomOpération StartSample ServiceContractName ITransactionSample Le workflow doit ressembler à ceci :
Cliquez sur le lien Définir... de l’activité Receive et effectuez les paramétrages suivants :
Faites glisser et déposez une activité Sequence dans la section de corps du TransactedReceiveScope. Dans l’activité Sequence, faites glisser et déposez deux activités WriteLine et définissez les propriétés Text conformément aux indications du tableau suivant.
Activité Valeur 1re WriteLine "Service: Receive Completed" 2e WriteLine "Service: Received = " + requestMessage Le workflow doit maintenant ressembler à ceci :
Faites glisser et déposez l’activité
PrintTransactionInfo
après la deuxième activité WriteLine du Corps de l’activité TransactedReceiveScope.Faites glisser et déposez une activité Assign après l’activité
PrintTransactionInfo
et définissez ses propriétés conformément aux indications du tableau suivant.Propriété Valeur À replyMessage Valeur "Service: Sending reply." Glissez et déposez une activité WriteLine après l’activité Assign et affectez à sa propriété Text la valeur "Service: Begin reply."
Le workflow doit maintenant ressembler à ceci :
Cliquez avec le bouton droit sur l’activité Receive, sélectionnez et collez Create SendReply après la dernière activité WriteLine. Cliquez sur le lien Définir... de l’activité
SendReplyToReceive
et effectuez les paramétrages suivants.Faites glisser et déposez une activité WriteLine après l’activité
SendReplyToReceive
et affectez à sa propriété Text la valeur « Service: Reply sent ».Faites glisser une activité WriteLine en bas du workflow et affectez à sa propriété Text la valeur "Service: Workflow ends, press ENTER to exit."
Le workflow de service terminé doit ressembler à ceci :
Implémenter le client de workflow
Ajoutez une nouvelle application de workflow WCF nommée
WorkflowClient
au projetCommon
. Pour ce faire, cliquez avec le bouton droit sur le projetCommon
, sélectionnez Ajouter, Nouvel élément..., puis Workflow sous Modèles installés et enfin Activité.Faites glisser et déposez une activité Sequence sur l’aire de conception.
Dans l’activité Sequence, faites glisser et déposez une activité WriteLine et affectez à sa propriété Text la valeur
"Client: Workflow starting"
. Le workflow doit maintenant ressembler à ceci :Faites glisser une activité TransactionScope après l'activité WriteLine. Sélectionnez l'activité TransactionScope, cliquez sur le bouton Variables et ajoutez les variables suivantes.
Faites glisser et déposez une activité Sequence dans le corps de l’activité TransactionScope.
Faites glisser une activité
PrintTransactionInfo
dans Sequence.Faites glisser et déposez une activité WriteLine après l’activité
PrintTransactionInfo
et affectez à sa propriété Text la valeur "Client: Beginning Send". Le workflow doit maintenant ressembler à ceci :Faites glisser une activité Send après l'activité Assign et définissez les propriétés suivantes :
Propriété Valeur EndpointConfigurationName workflowServiceEndpoint NomOpération StartSample ServiceContractName ITransactionSample Le workflow doit maintenant ressembler à ceci :
Cliquez sur le lien Définir... et effectuez les paramétrages suivants :
Cliquez avec le bouton droit sur l’activité Send et sélectionnez Create ReceiveReply. L'activité ReceiveReply sera automatiquement placée après l'activité Send.
Cliquez sur le lien Définir de l'activité ReceiveReplyForSend et effectuez les paramétrages suivants :
Faites glisser une activité WriteLine entre les activités Send et ReceiveReply et affectez à sa propriété Text la valeur "Client: Send complete."
Faites glisser et déposez une activité WriteLine après l’activité ReceiveReply et affectez à sa propriété Text la valeur "Client side: Reply received = " + replyMessage
Faites glisser une activité
PrintTransactionInfo
après l'activité WriteLine.Faites glisser et déposez une activité WriteLine à la fin du workflow et affectez à sa propriété Text la valeur "Client workflow ends". Le workflow du client terminé doit ressembler au diagramme suivant.
Générez la solution.
Créer l'application Service
Ajoutez à la solution un nouveau projet d'application console nommé
Service
. Ajoutez des références aux assemblys suivants :System.Activities.dll
System.ServiceModel.dll
System.ServiceModel.Activities.dll
Ouvrez le fichier Program.cs généré et le code suivant :
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(); }; }
Ajoutez le fichier app.config suivant au projet.
<?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>
Créer l’application cliente
Ajoutez à la solution un nouveau projet d'application console nommé
Client
. Ajoutez une référence à System.Activities.dll.Ouvrez le fichier program.cs et ajoutez le code suivant.
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; } }