Affichage des articles dont le libellé est Workflow Foundation. Afficher tous les articles
Affichage des articles dont le libellé est Workflow Foundation. Afficher tous les articles

21 octobre 2008

Workflow Foundation : Hello World

Pour ne pas déroger à la tradition du HelloWorld, nous allons créer pour ce premier exemple, un workflow qui écrit HelloWorld et votre prénom dans la console. Cet exemple très simple a le mérite de décrire les étapes fondamentales du développement et de l'exécution d'un workflow séquentiel. On va donc voir comment créer un workflow avec un template de Visual Studio 2008 prêt à l'emploi. Nous verrons ensuite comment il est hébergé et exécuté grâce au moteur de workflow.

Dans un premier temps, créez d'abord un nouveau projet dans une nouvelle solution. Choisissez le template "Sequential Workflow Console Application", nommez le projet "HelloWorld" et la solution "HelloWorldWorkflow". Le projet créé est de type Console comme laisse présager le titre du template.


Ce template crée un workflow s'appelant Workflow1 dans un fichier de type cs (j'ai oublié de dire que le langage utilisé est le c#). En cliquant sur le petit plus, vous voyez apparaître le fichier Workflow1.designer.cs qui est le fichier généré par le designer de Workflow. Comme dans d'autres éditeurs fournis par Visual Studio, la classe Workflow1 est partielle (mot clé partial avant le nom de la classe), ce qui permet de définir dans 2 fichiers séparés, voir plus, la classe en question. Cliquez droit sur Workflow1.cs, et en choisissant View Code, vous verrez l'autre partie de la classe définissant le workflow.

Le template a aussi créé un fichier Program.cs qui contient le code nécessaire pour instancier et exécuter le workflow généré.

Sur ce, revenez sur le designer, ouvrez la Toolbox (boite à outils) et faites glisser l'activité Code qui est de type CodeActivity. Déposez là dans le workflow. Vous devriez avoir le résultat suivant :


Voila, nous venons d'insérer notre 1ere activité dans un workflow. Cette activité a pour but d'exécuter la logique métier que vous devez définir par l'intermédiaire d'une méthode. D'ailleurs, vous avez surement du remarquer le point d'exclamation dans le rond rouge en haut à droite de l'activité. Il signifie que justement, nous n'avons pas encore défini la méthode à exécuter et qu'il est nécessaire de le faire. Pour cela, double cliquez sur l'activité. Cela a pour effet de créer une méthode nommée "codeActivity1_ExecuteCode" et d'afficher le code de la classe Workflow1.cs. Dans l'autre partie de la classe générée par le designer, cela a instancié un délégué qui prend en argument la méthode précédemment créée et ce délégué est ajouté à l'événement ExecuteCode de l'activité.

Définissons le code de la méthode codeActivity1_ExecuteCode. Ce code va écrire dans la console "Hello World " suivi d'un nom qui sera passé en paramètre au workflow. Ajoutez d'abord la propriété Nom de type string comme cela :



public string Nom
{
get;
set;
}

Cette façon d'écrire une propriété, qui est une nouveauté du Framework 3.5, permet de générer à la fois l'accesseur (get), le mutateur (set) et le champ de type string associé. C'est un raccourci d'écriture qui est fort utile lorsque vous n'avez pas besoin de définir un comportement spécial pour le mutateur ou l’accesseur. Rajoutons ensuite la ligne suivante dans la méthode codeActivity1_ExecuteCode :



private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Hello World " + Nom);
}

Le workflow HelloWorld est terminé. Pour information, voici le code du workflow en entier (Workflow1.cs) :



using System;
using System.Workflow.Activities;

namespace HelloWorld
{
public sealed partial class Workflow1: SequentialWorkflowActivity
{
public string Nom
{
get;
set;
}

public Workflow1()
{
InitializeComponent();
}

private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Hello World " + Nom);
}
}
}

Tout d'abord le code généré dans la méthode Main qui est le point d'entrée de notre application console, sert à instancier et exécuter le workflow précédemment créé. Pour cela, nous devons d'abord instancier le moteur de workflow représenté par la classe System.Workflow.Runtime.WorkflowRuntime. Cette classe ou plutôt l'objet instancié de cette classe, fournit l'environnement d'exécution pour tous les workflows de l'application. Une seule instance de cette classe est autorisée par domaine d'application, comprenez par application pour simplifier. Il est bien évidemment possible de définir plusieurs domaines d'application dans une même application, mais ce n'est pas le sujet ici. Voici le code d'initialisation du moteur :



using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
}

L'instanciation du moteur est faite dans un bloc using (instanciation) {Suite d'instruction} car le moteur dérive de l'interface IDisposable, qui oblige la classe qui en hérite de définir une méthode Dispose. Celle-ci s'occupe de terminer l'exécution des workflows en cours et de libérer la mémoire utilisée. A la fin du bloc using, cette méthode est donc appelée implicitement. Toute nouvelle instruction prend place entre les accolades du using.

Poursuivons notre description. La ligne suivante déclare un objet de type System.Threading.AutoResetEvent et l'instancie :



AutoResetEvent waitHandle = new AutoResetEvent(false);

Cet objet est utile pour synchroniser 2 threads d'exécution. Il permet de stopper l'exécution d'un thread jusqu'à ce qu'un évènement particulier soit détecté, évènement déclenché dans un autre thread. Le thread déclenchant l'évènement le fait bien évidement au moment opportun, c'est-à-dire lorsqu'il a terminé ce qu'il a à faire.

Mais pourquoi avons-nous besoin d'un tel objet pour exécuter notre workflow HelloWorld ? Et bien, c'est simple. Le comportement par défaut du moteur de workflow est de créer un thread par workflow exécuté. Ce comportement est bien sûr modifiable, mais pour l'heure, nous ferons avec. Si nous exécutons notre workflow sans utiliser un objet AutoResetEvent, alors notre application instancie effectivement le workflow, le lance, ce qui crée un thread dédié où le workflow est exécuté. Cependant juste après la ligne lançant l'exécution du workflow, le flux d'exécution continuera sans attendre la fin du workflow, sortira ensuite du bloc using, ce qui appelera la méthode Dispose implicitement, qui aura pour effet de détruire l'instance du moteur et du workflow par la même occasion. Nous n'aurons même pas la chance de voir afficher notre "HelloWorld" dans la console. Avec un objet AutoResetEvent, cela nous permet d'attendre tranquillement la fin du workflow.

Définissons 2 délégués anonymes que l'on affectera à l'évènement WorkflowCompleted et WorkflowTerminated du moteur de workflow (ici workflowRuntime). Le 1er évènement est déclenché lorsqu'un workflow exécuté par le moteur se termine avec succès. L'autre, WorkflowTerminate, est déclenché lorsqu'un workflow se termine à cause d'une exception. Voici les 2 délégués :



workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
{
waitHandle.Set();
};

workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};

Chacun d'eux exécute la méthode Set() de l'objet waitHandle. Cette instruction marche de pair avec la méthode WaitOne() du même objet. Comme expliqué plus haut, un workflow est exécuté dans son propre thread. Ainsi pour attendre la fin du workflow, nous utilisons la méthode WaitOne() qui attend que la méthode Set() soit exécutée dans un autre thread.

Les 2 délégués sont donc exécutés par le thread du workflow. Leur exécution permet de poursuivre l'éxecution de notre programme principal (bloquée jusqu'alors par WaitOne() de l'objet waitHandle).

Notez que le délégué associé à l'évènement WorkflowTerminated récupère l'erreur générée par le workflow et l'écrit dans la console.

A ce stade, il ne reste plus qu'à définir les arguments du workflow, c'est-à-dire le nom qui s'affichera à côté de "HelloWorld", de lancer le workflow et finalement d'attendre la fin de son exécution. Pour définir les arguments d'un workflow, WF permet d'utiliser une Hashtable ou Dictionnaire. Cette structure de données associe une valeur clé (ici de type string) avec un objet et cette association de données définie un élément du dictionnaire. Voici le code pour définir le paramètre de notre workflow :


Dictionary<string,object> arguments = new Dictionary<string,object>();
arguments.Add("Nom", "John Connor");

Il est possible de remplacer le nom ci-dessus par l'un de votre choix, ceci n'a aucune incidence sur le bon fonctionnement du workflow. Lors de la définition de notre workflow, nous avons défini une propriété Nom dans la classe partielle du workflow. La clé de notre argument dans le dictionnaire a le même nom que cette propriété. En fait, lorsque l'on passe le dictionnaire au moteur pour instancier notre workflow, celui-ci va, après avoir instancié le workflow, chercher, via la réflexion, la propriété du workflow s'appelant exactement "Nom" et va lui donner la valeur, ici "John Connor" en la castant, vu que c'est un objet dans le dictionnaire.

Il ne reste plus qu'à instancier le workflow et le lancer avec les 2 instructions suivantes :



WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(HelloWorld.Workflow1),arguments);
instance.Start();

La méthode CreateWorkflow prend en argument le type du workflow et le dictionnaire définissant les arguments. Elle renvoie un objet de type WorkflowInstance qui contient la méthode Start() permettant d'exécuter le workflow.

Finalement, ajoutez les 2 lignes suivantes pour attendre la fin d'exécution du workflow et de laisser le choix à l'utilisateur de fermer le programme.



waitHandle.WaitOne();
Console.ReadKey();

Si tout s'est bien passé, voici l'affichage que vous devriez avoir après avoir compilé et lancé le projet :


Pour une meilleure vision du code nécessaire à l'instanciation et l'exécution d'un workflow, voici l'intégralité du code :



using System;
using System.Threading;
using System.Workflow.Runtime;
using System.Collections.Generic;

namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) {waitHandle.Set();};
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};

Dictionary<string,object> arguments = new Dictionary<string,object>();
arguments.Add("Nom", "John Connor");

WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(HelloWorld.Workflow1),arguments);
instance.Start();

waitHandle.WaitOne();

Console.ReadKey();
}
}
}
}

8 octobre 2008

Qu'est ce qu'un workflow ?

Pour répondre à cette question, je vais faire l'analogie avec le code d'un développeur. Lorsque l'on développe, on définit le plus souvent une suite d'opérations ou de tâches. Cela peut être une insertion dans un journal d'événements ou dans une base de données. Ensuite, on ordonne ces opérations avec des instructions qui contrôlent le flux de notre programme. Par exemple, on utilise l'instruction :

If (condition)
{Liste d'opération 1}
else {Liste d'opération 2}


ou un bloc :

While (Condition)
{Liste d'opérations}


pour traiter des données en boucle.

Chaque décision dans le code, que ce soit en C#, Java, C/C++, Javascript etc ..., est matérialisée par des conditions. Tout code source possède 3 types d'instruction:
  • Les instructions d'exécution qui peuvent être une écriture dans une base de données ou encore dans un journal d'évènements, l'envoi d'un mail, ou plus généralement toute instruction qui exécute une action.
  • Les instructions définissant le flux d'exécution comme le bloc if (condition) {instructions} else {instructions}. Ce sont toutes les instructions qui ont une influence sur le flux d'exécution du programme.
  • Les règles qui définissent les conditions dans les flux d'exécution.

Voici un exemple montrant les 3 types les 3 types d'instruction:

Les concepteurs de Workflow Foundation ont identifié ces 3 concepts et les ont intégrés dans Workflow Foundation. Un workflow n'est rien d'autre qu'une suite d'instructions d'exécution représentée par des activités (Custom Activity, Code Activity, InvokeWebService, etc…). Ces instructions d'exécution ou activités sont elles-mêmes comprises dans des activités définissant le flux d'exécution du programme (IfElseActivity, WhileActivity, ParallelActivity, ReplicatorActivity, etc…). Pour définir les conditions dans les activités définissant le flux d'exécution, on met en place des règles (rules en anglais). Pour illustrer cela, voici un workflow reprenant la même logique que l'exemple précédent :
Premièrement, le workflow est représenté de manière graphique. Les deux exemples réalisent exactement la même chose. Cependant, dans le premier exemple, tout a été défini avec un langage textuel qui est le C# alors que dans le deuxième, tout a été quasiment définit de manière graphique en utilisant le drag'n drop et la fenêtre de propriété de Visual Studio 2008.
Aujourd'hui, c'est dans l'air du temps d'utiliser ce que l'on appelle des langages graphiques ou des DSL (Domain Specific Language). Le plus célèbre d'entre eux est le designer de classes intégré dans toutes les éditions de Visual Studio. Workflow Foundation propose ainsi de définir votre propre langage graphique pour définir les workflows. Pour cela, nous avons à notre disposition un ensemble d'activités que l'on peut glisser et déplacer dans la fenêtre définissant le workflow. Voici un aperçu de la Toolbox regroupant les différentes activités fournies par WF :

Il est aussi possible de définir un workflow par le code, en instanciant les bons objets, ou avec du XAML (rebaptisé XOML dans WF, pour le différencier du XAML de WPF). Le XAML est un langage basé sur XML permettant d'instancier et de paramétrer des objets.
Lorsqu'on crée un nouveau workflow, nous avons le choix avec 2 implémentations de workflow :
  • Workflow basé sur du code uniquement
  • Workflow basé sur du XAML avec un fichier de code associé

Théoriquement, il est aussi possible définir un workflow basé uniquement sur du XAML en supprimant le fichier de code associé, Visual Studio 2005 ou 2008 ne propose pas ce template. L'intérêt principal d'un workflow définit uniquement en XAML, est que l'on peut le modifier à la main ou via un éditeur spécialisé sans avoir besoin de recompiler l'application. Cela laisse penser qu'il est possible développer des applications avec des workflows, et que votre client pourra via un éditeur de workflow, modifier par la suite le comportement de son programme en éditant lui-même ses workflows. Bien sûr, il devra avoir une connaissance du vocabulaire métier défini dans le workflow, mais comme celui-ci est la matérialisation d'un processus de l'entreprise, cela devrait lui être familier. Dans tous les cas, c'est beaucoup plus simple et plus réaliste que de modifier du code et de le recompiler.
Une autre différence fondamentale lorsqu'on utilise WF est que la logique définissant ce que l'on fait est séparée de la logique qui définit quand on le fait. C'est-à-dire que l'on peut changer toute la logique de séquencement ou d'ordonnancement d'un workflow sans affecter les instructions d'exécution. Pour cela, on sélectionne la ou les activités voulues et on les déplace à l'endroit souhaité dans le workflow. Avec un langage textuel, cela se résumerai à un ensemble de couper/coller quelque peu hasardeux et pas toujours très simple à réaliser.
Comme la logique d'ordonnancement est définie de manière déclarative et qu'en fait chaque activité correspond à une classe, il est tout à fait possible de modifier à l'exécution, c'est-à-dire dynamiquement, un workflow. Par exemple, on peut rajouter de nouvelles instructions d'exécution ou des instructions définissant le flux d'exécution à un endroit donné. Il suffit simplement d'instancier les activités voulues, de les paramétrer et de les insérer là où on le souhaite. Comparé à un langage textuel, cela pourrait être synonyme d'injection de code (via de la POA par exemple), ce qui est quand même bien plus complexe à réaliser.

12 mai 2008

Pourquoi développer des workflows avec Workflow Foundation ?

Tout d'abord, Workflow Foundation est un Framework permettant de réaliser des workflows tout en respectant une bonne productivité. Dorénavant, si vous pensez créer des workflows dans vos prochains projets, il est clair que vous devez penser à Workflow Foundation, surtout si vous êtes familier du Framework .Net. Si vous êtes issu du monde J2EE, PHP ou autre, il est tout à fait possible et Microsoft a travaillé dessus, de mettre en place un service quelconque, qui expose par des méthodes vos propres workflows. Tout ceci est réalisé avec un minimum d'effort si bien sûr vous disposez de Visual Studio 2005 ou 2008.


WF propose un ensemble d'outils cohérent pour concevoir vos workflows. L'investissement requis pour comprendre WF et mettre en place un workflow est vite rentabilisé. L'éditeur de workflow fourni, permet un développement visuel et non plus textuel de vos processus. Cela se résume le plus souvent à du glisser/déplacer et à de la configuration de propriétés. Il est alors très facile à comprendre que cela augmente la maintenabilité de vos workflows.


Cet environnement graphique permet d'exposer et de discuter de vos workflows avec le client. En effet, un graphique est bien plus parlant que du texte et rien que penser à montrer du code à un client est complètement absurde. Un processus conçu visuellement avec WF est vite compréhensible par quiconque comprenant le vocabulaire métier exposé.


Lorsque vous développez vos propres workflows, vous êtes souvent amené à créer des custom activity (activités personnalisées). Ces composants ont la particularité d'être fortement réutilisables, d'une part dans un même projet mais aussi entre projets (du moment qu'ils sont indépendants). Il est tout à fait envisageable de créer une bibliothèque d'activité, que chaque développeur et architecte utilisera et alimentera.

Le Framework est fourni avec une suite d'activités standards qui couvrent un ensemble important de cas conventionnels. D'une part, ces activités vous aiguillent dans la conception de vos workflows, et d'autre part vous bénéficiez de l'expérience de Microsoft dans la matérialisation de vos processus. De plus, avec le service de persistance fourni, qui permet le stockage de vos workflows en base de données, cela a pour effet d'augmenter la fiabilité et les performances de vos applications. Le service de trace aussi fourni, permet de suivre l'exécution de vos workflows en quasi temps réel.