Mostefai Mohammed Amine's Blog

Say That I Was Here !

Tutoriel 1.1 : Découverte de Workflow Foundation 4.5

L’objectif de ce tutoriel, relatif au premier cours, est de découvrir la structure d’une application WF 4.5 et les différentes façons de créer un workflow pour ensuite, l’exécuter. Etape 1 – Ouverture de la solution et son exécution L’objectif de cette étape est d’ ouvrir la solution de démonstration de ce module. Lancer VS 2012 Choisir Fichier -> Ouvrir -> Projet Solution Naviguez jusqu’au répertoire de démonstrations Ouvrez la solution « Demo1.sln », il s’agit d’une application console En utilisant l’explorateur de solutions, ouvrez le fichier « Program.cs » Le listing de ce fichier devrait être comme ceci : class Program { static void Main(string[] args) { Console.WriteLine("--------------------------------------"); Console.WriteLine("Workflows séquentiels"); Console.WriteLine("--------------------------------------"); var w1 = new TransactionBancaireWorkflow(); w1.Montant = 55000; IDictionary<string, object> result = WorkflowInvoker.Invoke(w1); Console.WriteLine("Commission : {0}", result["Commission"]); Console.WriteLine("--------------------------------------"); Console.WriteLine("Diagrammes de flux"); Console.WriteLine("--------------------------------------"); var w2 = new TransactionBancaireDFWorkflow(); w2.Montant = 9000; result = WorkflowInvoker.Invoke(w2); Console.WriteLine("Commission : {0}", result["Commission"]); Console.WriteLine("--------------------------------------"); Console.WriteLine("Diagrammes de machine d'états"); Console.WriteLine("--------------------------------------"); var w3 = new TransactionValidationWorkflow(); w3.Montant = 59000; var app = new WorkflowApplication(w3); app.Run(); Console.WriteLine("appuyez sur une touche pour valider le workflow"); Console.ReadKey(); app.ResumeBookmark("signet", null); Console.WriteLine("--------------------------------------"); Console.WriteLine("Workflow créé par code"); Console.WriteLine("--------------------------------------"); var w4 = GetCodeWorkflow(); WorkflowInvoker.Invoke(w4); Console.WriteLine("--------------------------------------"); Console.WriteLine("Workflow chargé à partir d'un fichier XML"); Console.WriteLine("--------------------------------------");   var w5 = ActivityXamlServices.Load("wf5.xaml"); WorkflowInvoker.Invoke(w5); Console.ReadKey(); }   private static Activity GetCodeWorkflow() { var workflow = new Sequence() { }; workflow.Activities.Add(new WriteLine() { Text = "Un workflow créé par code !" }); return workflow; } } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Exécutez l’application en appuyant sur F5 Lorsqu’un message demandant d’appuyer sur une touche apparaît, appuyez sur n’importe quelle touche Etape 2 – Accès à un workflow séquentiel L’objectif de cette étape est de voir la structure et de comprendre l’exécution d’un workflow séquentiel. A partir de l’explorateur de solutions, ouvrez le fichier « TransactionBancaireWorkflow.xaml » Remarquez le workflow chargé dans le designer graphique Remarquez que le workflow est décomposé d’une instruction conditionnelle (if) composée à son tour de deux branches « if » et « else » La première branche calcule la commission à partir du montant (0.2%) si le montant dépasse 50000. Ensuite elle affiche le message indiquant que l’opération a besoin de validation. La deuxième branche affecte une valeur de zéro à la variable commission. En dessous de la fenêtre du designer, cliquez sur le lien « Arguments » Remarquez la présence de deux arguments, l’un appelé « Montant » et l’autre « Commission ». Les deux arguments sont de type « Decimal » Remarquez que l’argument « Montant » est en entrée et que l’argument « Commission » est en sortie. Revenez au fichier « Program.cs », examinez comment un workflow a été instancié : var w1 = new TransactionBancaireWorkflow() ; .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Les workflows sont créés comme n’importe quel object C#, en utilisant l’opérateur « new » L’instruction suivante montre comment un environnement externe (le programme) fournit les valeurs des arguments en entrée du workflow (Montant). Il s’agit d’une simple affectation car un alias de propriété est créé pour « Montant ». w1.Montant = 55000 ; L’instruction suivante exécute le workflow. Une nouveauté de WF 4.5 est que c’est devenu très simple d’exécuter un workflow, juste en utilisant la classe « WorkflowInvoker » et la méthode « Invoke ». Remarquez que la méthode « Invoke » renvoir un dictionnaire d’objets. Il s’agit des arguments en sortie créés par le workflow. IDictionary<string, object> result = WorkflowInvoker.Invoke(w1); .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Pour accéder ensuite à la valeur de commission, nous utiliserons la variable “result” Console.WriteLine("Commission : {0}", result["Commission"]); .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Changez le workflow en changeant le montant maximum (50000) dans le designer et/ ou en changeant l’argument « Montant » fourni dans « Program.cs à Main » Etape 3 : Accès à un workflow diagramme de flux L’objectif de cette étape est de découvrir un autre type de workflows : les diagrammes de flux En utilisant l’explorateur de solution, ouvrez le fichier « TransactionBancaireDFWorkflow.xaml » Remarquez que le workflow est graphiquement différent du workflow précédent mais fonctionnellement équivalent Remarquez que ce workflo a les mêmes arguments que le workflow précédent. De la même façon, l’argument « Montant » est fourni en utilisant une affectation et le workflow est exécuté en utilisant « WorkflowInvoker » var w2 = new TransactionBancaireDFWorkflow(); w2.Montant = 9000; result = WorkflowInvoker.Invoke(w2); Console.WriteLine("Commission : {0}", result["Commission"]); .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Changez le montant en entrée et / ou le plafond de validation dans le workflow puis rééexéutez le programme Etape 4 : Accès à un workflow d’états L’objectif de cette étape est de découvrir les workflows de machine d’état qui permettant de transiter un workflow d’un état à un autre jusqu’à atteindre un état final. L’objectif est d’utiliser aussi certaines fonctionnalités du designer. En utilisant l’explorateur de solutions, ouvrez « TransactionValidationWorkflow.xaml » Remarquez que le workflow est graphiquement différent des deux premiers Remarquez que le workflow démarre par un état initial A partir de l’état initial, la transition « T1 » met le workflow dans un état « AttenteVal » tandis que la transition « T2 » le met dans un état final Cliquez sur la transition « T1 » puis sur la fenêtre de propriétés Remarquez que la transition « T1 » a pour condition que le montant soit supérieur à 50000 De la même façon, consultez la condition de la transition « T2 » Affichez les arguments du workflows Vérifiez que le workflow possède un argument en entrée « Montant » de type « Decimal » Dans le workflow, doublez-cliquez sur l’état « AttenteVal » Dans la zone de texte « Zoom » en dessous, entrez « 70% » Remettez le zoom à « 100% » Remarquez que le workflow dépasse le cadre de la fenête Naviguez dans le workflow en utilisant le bouton « Vue d’ensemble » ou « Overview » située en bas à droite de l’écran Remarquez en haut de l’écran, les liens permettant de naviguer dans les niveaux du workflows. Le lien indique qu’on est dans l’activité « AttenteVal » qui est elle-même enfant d’une autre activité « StateMachine » représentant le workflow Remarquez que l’activité est composée d’une entrée et d’une sortie (entrée de l’état et sortie de l’état). L’entrée est composée d’une séquence qui est elle-même composée d’un affiche puis d’une activité Pick. A l’intérieur de la zone « Trigger » de Pick, il y a une activité « Attendre ». L’activité « Attendre » n’est pas une activité native. C’est une activité que nous avons nous-mêmes développés. Son implémentation est dans le fichier « Attendre.cs » public sealed class Attendre : NativeActivity<object> { protected override void Execute(NativeActivityContext context) { context.CreateBookmark("signet");     }   protected override bool CanInduceIdle { get { return true; } } } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Remarquez la présence de l’activité « Attendre » dans la boîte à outils L’activité attendre crée un signet (Bookmark) qui bloque le workflow en attendant une action de l’utilisateur . Dès qu’un utilisateur tape une touche, le workflow reprend son activité. Remarque dans « Program.cs » que le workflow n’est pas exécuté par WorkflowInvoker mais par la classe « WorkflowApplication » var w3 = new TransactionValidationWorkflow(); w3.Montant = 59000; var app = new WorkflowApplication(w3); app.Run(); Console.WriteLine("appuyez sur une touche pour valider le workflow"); Console.ReadKey(); app.ResumeBookmark("signet", null); .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Etape 5 : Accès à un workflow créé par code L’objectif de cette étape est de voir comment un workflow est créé par code sans passer par le designer. A partir de l’explorateur de solutions, ouvrez le fichier « Program.cs » Consultez la méthode « GetCodeWorkflow » private static Activity GetCodeWorkflow() { var workflow = new Sequence() { }; workflow.Activities.Add(new WriteLine() { Text = "Un workflow créé par code !" }); return workflow; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } La méthode crée une activité de type « Sequence » puis ajoute une activité « WriteLine » a ses enfants Le workflow est normalement exécuté par « WorkflowInvoker » comme pour les workflows créés par designer var w4 = GetCodeWorkflow(); WorkflowInvoker.Invoke(w4); .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Etape 6 : Chargement d’un workflow à partir d’un fichier L’objectif de cette étape est de créer un workflow qui a été ultérieurement enregistré dans un fichier « XAML ». Dans l’explorateur de solutions, cliquez sur le bouton droit sur le projet « Demo1 » Cliqez sur « Ouvrir le dossier dans l’explorateur de fichiers » Dans l’explorateur accédez au dossier « bin à debug » Ouvrez le fichier « wf5.xaml » dans le bloc notes et examinez son contenu <Activity mc:Ignorable="sap sap2010 sads" x:Class="Demo1.XmlWorkflow" sap2010:ExpressionActivityEditor.ExpressionActivityEditor="C#" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation" xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <TextExpression.NamespacesForImplementation> <sco:Collection x:TypeArguments="x:String"> <x:String>System</x:String> <x:String>System.Collections.Generic</x:String> <x:String>System.Data</x:String> <x:String>System.Linq</x:String> <x:String>System.Text</x:String> </sco:Collection> </TextExpression.NamespacesForImplementation> <TextExpression.ReferencesForImplementation> <sco:Collection x:TypeArguments="AssemblyReference"> <AssemblyReference>Microsoft.CSharp</AssemblyReference> <AssemblyReference>System</AssemblyReference> <AssemblyReference>System.Activities</AssemblyReference> <AssemblyReference>System.Core</AssemblyReference> <AssemblyReference>System.Data</AssemblyReference> <AssemblyReference>System.Runtime.Serialization</AssemblyReference> <AssemblyReference>System.ServiceModel</AssemblyReference> <AssemblyReference>System.ServiceModel.Activities</AssemblyReference> <AssemblyReference>System.Xaml</AssemblyReference> <AssemblyReference>System.Xml</AssemblyReference> <AssemblyReference>System.Xml.Linq</AssemblyReference> <AssemblyReference>mscorlib</AssemblyReference> <AssemblyReference>Demo1</AssemblyReference> </sco:Collection> </TextExpression.ReferencesForImplementation> <WriteLine Text="Un workflow chargé à partir d'un fichier" sap2010:WorkflowViewState.IdRef="WriteLine_1" sads:DebugSymbol.Symbol="dzxGOlxGb3JtYXRpb25XRlxEZW1vc1xDaGFwaXRyZSAxXERlbW8xXERlbW8xXFhtbFdvcmtmbG93LnhhbWwCIwMjlwECAQEjEyMzAgEC" /> <sap2010:WorkflowViewState.IdRef>Demo1.XmlWorkflow_1</sap2010:WorkflowViewState.IdRef> <sap2010:WorkflowViewState.ViewStateManager> <sap2010:ViewStateManager> <sap2010:ViewStateData Id="WriteLine_1" sap:VirtualizedContainerService.HintSize="211,62" /> <sap2010:ViewStateData Id="Demo1.XmlWorkflow_1" sap:VirtualizedContainerService.HintSize="251,142" /> </sap2010:ViewStateManager> </sap2010:WorkflowViewState.ViewStateManager> </Activity> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Revenez dans Visual Studio puis ouvrez le fichier « Program.cs » Remarquez comment le workflow est chargé à partir du fichier précédent var w5 = ActivityXamlServices.Load("wf5.xaml"); WorkflowInvoker.Invoke(w5); Dans le fichier xaml, changez la propriété Text de la balise WriteLine Rééxécutez « Demo1 » pour voir le résultat Conclusion Et voilà, en mode découverte, vous avez vu comment exécuter plusieurs types de workflows et aussi les différents modes de création. Je reviendrai avec d’autres tutoriaux qui iront plus en détail sur la conception de workflows. Vous pouvez télécharger la démo ici Enjoy !