A Developer's Introduction to Windows Workflow Foundation (WF) in .NET 4
Matt Milner, Pluralsight
Novembre 2009
Mise à jour vers la version : avril 2010
Vue d’ensemble
Comme le savent les développeurs de logiciels, l’écriture d’applications peut être difficile, et nous sommes constamment à la recherche d’outils et de frameworks pour simplifier le processus et nous aider à nous concentrer sur les défis métier que nous essayons de résoudre. Nous sommes passés de l’écriture de code dans des langages machine tels que l’assembleur à des langages de niveau supérieur comme C# et Visual Basic qui facilitent notre développement, suppriment les problèmes de niveau inférieur tels que la gestion de la mémoire et augmentent notre productivité en tant que développeurs. Pour les développeurs Microsoft, le passage à .NET permet au Common Language Runtime (CLR) d’allouer de la mémoire, de nettoyer les objets inutiles et de gérer des constructions de bas niveau comme des pointeurs.
Une grande partie de la complexité d’une application réside dans la logique et le traitement qui se déroule en arrière-plan. Des problèmes tels que l’exécution asynchrone ou parallèle et la coordination générale des tâches pour répondre aux demandes des utilisateurs ou aux demandes de service peuvent rapidement amener les développeurs d’applications à redescendre dans le codage de bas niveau des handles, des rappels, de la synchronisation, etc. En tant que développeurs, nous avons besoin de la même puissance et de la même flexibilité d’un modèle de programmation déclaratif pour les éléments internes d’une application que pour l’interface utilisateur dans Windows Presentation Foundation (WPF). Windows Workflow Foundation (WF) fournit l’infrastructure déclarative pour la création d’une logique d’application et de service, et offre aux développeurs un langage de niveau supérieur pour la gestion des tâches asynchrones, parallèles et d’autres traitements complexes.
Le fait de disposer d’un runtime pour gérer la mémoire et les objets nous a permis de nous concentrer davantage sur les aspects métier importants de l’écriture de code. De même, avoir un runtime capable de gérer les complexités de la coordination du travail asynchrone fournit un ensemble de fonctionnalités qui améliorent la productivité des développeurs. WF est un ensemble d’outils pour déclarer votre flux de travail (votre logique métier), des activités permettant de définir la logique et le flux de contrôle, et un runtime pour l’exécution de la définition d’application résultante. En bref, WF consiste à utiliser un langage de niveau supérieur pour l’écriture d’applications, dans le but de rendre les développeurs plus productifs, les applications plus faciles à gérer et de les modifier plus rapidement à implémenter. Le runtime WF exécute non seulement vos flux de travail pour vous, mais il fournit également des services et des fonctionnalités importants lors de l’écriture de la logique d’application, telles que la persistance de l’état, le signet et la reprise de la logique métier, qui mènent à l’agilité des threads et des processus permettant un scale-up et un scale-out des processus métier.
Pour plus d’informations conceptuelles sur la façon dont l’utilisation de WF pour créer vos applications peut vous rendre plus productif, je vous recommande de lire « The Workflow Way » de David Chappell, qui se trouve dans la section Ressources supplémentaires.
Nouveautés de WF4
Dans la version 4 de Microsoft® .NET Framework, Windows Workflow Foundation apporte une quantité importante de modifications par rapport aux versions précédentes de la technologie fournie dans le cadre de .NET 3.0 et 3.5. En fait, l’équipe a revisité le cœur du modèle de programmation, du runtime et des outils et a réécrit chacun d’eux pour augmenter les performances et la productivité, ainsi que pour répondre aux commentaires importants recueillis par les engagements des clients à l’aide des versions précédentes. Les modifications importantes apportées ont été nécessaires pour offrir la meilleure expérience aux développeurs qui adoptent WF et pour permettre à WF de continuer à être un composant de base solide sur lequel vous pouvez vous appuyer dans vos applications. Je vais présenter les changements de haut niveau ici, et tout au long de l’article, chaque sujet obtiendra un traitement plus approfondi.
Avant de continuer, il est important de comprendre que la compatibilité descendante était également un objectif clé dans cette version. Les nouveaux composants d’infrastructure se trouvent principalement dans les assemblys System.Activities.*, tandis que les composants d’infrastructure à compatibilité descendante se trouvent dans les assemblys System.Workflow.*. Les assemblys System.Workflow.* font partie du .NET Framework 4 et fournissent une compatibilité descendante complète, ce qui vous permet de migrer votre application vers .NET 4 sans modifier votre code de flux de travail. Tout au long de ce document, je vais utiliser le nom WF4 pour faire référence aux nouveaux composants trouvés dans les assemblys System.Activities.* et WF3 pour faire référence aux composants trouvés dans les assemblys System.Workflow.*.
Concepteurs
L’un des domaines d’amélioration les plus visibles se trouve dans le concepteur de flux de travail. La convivialité et les performances étaient des objectifs clés pour l’équipe pour la version VS 2010. Le concepteur prend désormais en charge la possibilité de travailler avec des workflows beaucoup plus volumineux sans dégradation des performances et les concepteurs sont tous basés sur Windows Presentation Foundation (WPF), tirant pleinement parti de la riche expérience utilisateur que l’on peut créer avec l’infrastructure d’interface utilisateur déclarative. Les développeurs d’activités utilisent XAML pour définir l’apparence de leurs activités et interagir avec les utilisateurs dans un environnement de conception visuelle. En outre, le réhébergement du concepteur de flux de travail dans vos propres applications pour permettre aux non-développeurs d’afficher et d’interagir avec vos flux de travail est désormais beaucoup plus facile.
Data Flow
Dans WF3, le flux de données dans un workflow était opaque. WF4 fournit un modèle clair et concis pour le flux de données et l’étendue de l’utilisation d’arguments et de variables. Ces concepts, familiers à tous les développeurs, simplifient à la fois la définition du stockage de données, ainsi que le flux des données à l’entrée et à l’extérieur des workflows et des activités. Le modèle de flux de données rend également plus évidentes les entrées et sorties attendues d’une activité donnée et améliore les performances du runtime à mesure que les données sont plus faciles à gérer.
Organigramme
Une nouvelle activité de flux de contrôle appelée Organigramme a été ajoutée pour permettre aux développeurs d’utiliser le modèle Organigramme pour définir un workflow. L’organigramme ressemble plus étroitement aux concepts et aux processus de pensée que de nombreux analystes et développeurs traversent lors de la création de solutions ou de la conception de processus métier. Par conséquent, il était logique de fournir une activité pour faciliter la modélisation de la réflexion conceptuelle et de la planification qui avaient déjà été effectuées. L’organigramme active des concepts tels que le retour aux étapes précédentes et le fractionnement d’une logique basée sur une seule condition, ou une logique Switch/Case.
Modèle de programmation
Le modèle de programmation WF a été repensé pour le rendre à la fois plus simple et plus robuste. L’activité est le type de base principal dans le modèle de programmation et représente à la fois les flux de travail et les activités. En outre, vous n’avez plus besoin de créer un WorkflowRuntime pour appeler un workflow, vous pouvez simplement créer un instance et l’exécuter, ce qui simplifie les scénarios de test unitaire et d’application dans lesquels vous ne souhaitez pas avoir à configurer un environnement spécifique. Enfin, le modèle de programmation de flux de travail devient une composition entièrement déclarative des activités, sans code à côté, ce qui simplifie la création de flux de travail.
Intégration de Windows Communication Foundation (WCF)
Les avantages de WF s’appliquent très certainement à la création de services et à la consommation ou à la coordination des interactions de service. Beaucoup d’efforts ont été déployés pour améliorer l’intégration entre WCF et WF. De nouvelles activités de messagerie, la corrélation des messages et l’amélioration de la prise en charge de l’hébergement, ainsi que la définition de service entièrement déclarative sont les principaux domaines d’amélioration.
Prise en main avec workflow
La meilleure façon de comprendre WF est de commencer à l’utiliser et à appliquer les concepts. Je vais aborder plusieurs concepts de base autour des fondements du flux de travail, puis passer en revue la création de quelques flux de travail simples pour illustrer comment ces concepts sont liés les uns aux autres.
Structure de flux de travail
Les activités sont les blocs de construction de WF et toutes les activités dérivent finalement de l’activité. Une note de terminologie : les activités sont une unité de travail dans WF. Les activités peuvent être composées ensemble en activités plus grandes. Lorsqu’une activité est utilisée comme point d’entrée de niveau supérieur, elle est appelée « Workflow », tout comme Main est simplement une autre fonction qui représente un point d’entrée de niveau supérieur pour les programmes CLR. Par exemple, la figure 1 montre un flux de travail simple en cours de création de code.
Séquence s = nouvelle séquence
{
Activités = {
new WriteLine {Text = « Hello"},
new Sequence {
Activités =
{
new WriteLine {Text = « Workflow"},
new WriteLine {Text = « World"}
}
}
}
};
Figure 1 : Flux de travail simple
Notez dans la figure 1 que l’activité Sequence est utilisée comme activité racine pour définir le style de flux de contrôle racine pour le flux de travail. N’importe quelle activité peut être utilisée comme racine ou workflow et exécutée, même une simple writeline. En définissant la propriété Activities sur la séquence avec une collection d’autres activités, j’ai défini la structure de flux de travail. En outre, les activités enfants peuvent avoir des activités enfants, créant une arborescence d’activités qui composent la définition globale du flux de travail.
Modèles de flux de travail et Designer de flux de travail
WF4 est fourni avec de nombreuses activités et Visual Studio 2010 inclut un modèle pour définir des activités. Les deux activités les plus courantes utilisées pour le flux de contrôle racine sont Séquence et Organigramme. Bien que n’importe quelle activité puisse être exécutée en tant que flux de travail, ces deux modèles de conception fournissent les modèles de conception les plus courants pour la définition de la logique métier. La figure 2 montre un exemple de modèle de flux de travail séquentiel définissant le traitement d’une commande reçue, enregistrée, puis envoyée à d’autres services.
Figure 2 : Conception de flux de travail séquentiel
Le type de flux de travail organigramme est introduit dans WF4 pour répondre aux demandes courantes d’utilisateurs existants, telles que la possibilité de revenir aux étapes précédentes d’un flux de travail et parce qu’il ressemble plus étroitement à la conception conceptuelle effectuée par les analystes et les développeurs qui travaillent sur la définition de la logique métier. Par exemple, considérez un scénario impliquant une entrée utilisateur dans votre application. En réponse aux données fournies par l’utilisateur, votre programme doit continuer dans le processus ou revenir à une étape précédente pour demander à nouveau l’entrée. Avec un flux de travail séquentiel, cela implique quelque chose de similaire à ce qui est illustré dans la figure 3 où une activité DoWhile est utilisée pour continuer le traitement jusqu’à ce qu’une condition soit remplie.
Figure 3 : Séquentiel pour la branche de décision
La conception de la figure 3 fonctionne, mais en tant que développeur ou analyste examinant le flux de travail, le modèle ne représente pas la logique ou les exigences d’origine qui ont été décrites. Le flux de travail organigramme de la figure 4 donne un résultat technique similaire au modèle séquentiel utilisé dans la figure 3, mais la conception du flux de travail correspond plus étroitement à la réflexion et aux exigences définies à l’origine.
Figure 4 : Flux de travail organigramme
Flux de données dans les workflows
La première pensée que la plupart des gens ont lorsqu’ils pensent au flux de travail est le processus métier ou le flux de l’application. Toutefois, les données qui pilotent le processus et les informations collectées et stockées pendant l’exécution du flux de travail sont tout aussi critiques que le flux de travail. Dans WF4, le stockage et la gestion des données ont été un domaine de premier plan de la conception.
Il existe trois concepts main à comprendre en ce qui concerne les données : variables, arguments et expressions. Les définitions simples pour chacun sont que les variables sont destinées au stockage des données, les arguments sont pour la transmission de données et les expressions sont destinées à la manipulation de données.
Variables : stockage de données
Les variables dans les flux de travail sont très similaires à celles que vous utilisez dans les langages impératifs : elles décrivent un emplacement nommé pour les données à stocker et suivent certaines règles d’étendue. Pour créer une variable, vous devez d’abord déterminer à quelle étendue la variable doit être disponible. Tout comme vous pouvez avoir des variables dans le code qui sont disponibles au niveau de la classe ou de la méthode, vos variables de workflow peuvent être définies à différentes étendues. Considérez le flux de travail dans la figure 5. Dans cet exemple, vous pouvez définir une variable au niveau racine du flux de travail ou à l’étendue définie par l’activité de séquence Collecter les données de flux.
Figure 5 : Variables limitées aux activités
Arguments : passage de données
Les arguments sont définis sur les activités et définissent le flux de données entrantes et sortantes de l’activité. Vous pouvez penser aux arguments aux activités comme vous utilisez des arguments pour des méthodes dans le code impératif. Les arguments peuvent être In, Out ou In/Out et avoir un nom et un type. Lors de la génération d’un flux de travail, vous pouvez définir des arguments sur l’activité racine qui permet aux données d’être transmises à votre flux de travail lorsqu’elles sont appelées. Vous définissez des arguments sur le flux de travail comme vous le faites des variables à l’aide de la fenêtre d’argument.
Lorsque vous ajoutez des activités à votre flux de travail, vous devez configurer les arguments pour les activités, et cela se fait principalement en référençant des variables dans l’étendue ou en utilisant des expressions, que je vais aborder ensuite. En fait, la classe de base Argument contient une propriété Expression qui est une Activité qui retourne une valeur du type d’argument, de sorte que toutes ces options sont liées et s’appuient sur Activity.
Lorsque vous utilisez le concepteur de flux de travail pour modifier des arguments, vous pouvez taper des expressions représentant des valeurs littérales dans la grille de propriétés, ou utiliser des noms de variables pour référencer une variable dans l’étendue, comme illustré dans la figure 6, où emailResult et emailAddress sont des variables définies dans le flux de travail.
Figure 6 : Configuration des arguments sur les activités
Expressions : action sur les données
Les expressions sont des activités que vous pouvez utiliser dans votre workflow pour fonctionner sur des données. Les expressions peuvent être utilisées dans les endroits où vous utilisez Activity et que vous êtes intéressé par une valeur de retour, ce qui signifie que vous pouvez utiliser des expressions pour définir des arguments ou pour définir des conditions sur des activités telles que les activités While ou If. N’oubliez pas que la plupart des éléments de WF4 dérivent de l’activité et des expressions ne sont pas différents, ils sont un dérivé de Activity<TResult> , ce qui signifie qu’ils retournent une valeur d’un type spécifique. En outre, WF4 inclut plusieurs expressions courantes pour faire référence à des variables et des arguments, ainsi qu’à des expressions Visual Basic. En raison de cette couche d’expressions spécialisées, les expressions que vous utilisez pour définir des arguments peuvent inclure du code, des valeurs littérales et des références de variables. Le tableau 1 fournit un petit échantillonnage des types d’expressions que vous pouvez utiliser lors de la définition d’arguments à l’aide du concepteur de flux de travail. Lors de la création d’expressions dans le code, il existe plusieurs autres options, notamment l’utilisation d’expressions lambda.
Expression | Type d’expression |
---|---|
« Hello World » |
Valeur de chaîne littérale |
10 |
Valeur Int32 littérale |
System.String.Concat(« hello », « », « world ») |
Appel de méthode impérative |
« hello » & « world » |
Expression Visual Basic |
argInputString |
Référence d’argument (nom de l’argument) |
varResult |
Référence de variable (nom de la variable) |
« hello: " & argInputString |
Littéraux et arguments/variables mixtes |
Tableau 1 : Exemples d’expressions
Création de votre premier workflow
Maintenant que j’ai abordé les concepts fondamentaux liés à l’activité et au flux de données, je peux créer un workflow à l’aide de ces concepts. Je vais commencer par un flux de travail hello world simple pour me concentrer sur les concepts plutôt que sur la véritable proposition de valeur de WF. Pour commencer, créez un projet de test unitaire dans Visual Studio 2010. Pour utiliser le noyau de WF, ajoutez une référence à l’assembly System.Activities et ajoutez des instructions using pour System.Activities, System.Activities.Statements et System.IO dans le fichier de classe de test. Ajoutez ensuite une méthode de test comme illustré dans la figure 7 pour créer un workflow de base et l’exécuter.
[TestMethod]
public void TestHelloWorldStatic()
{
StringWriter writer = new StringWriter();
Console.SetOut(writer);
Séquence wf = nouvelle séquence
{
Activités = {
new WriteLine {Text = « Hello"},
new WriteLine {Text = « World"}
}
};
WorkflowInvoker.Invoke(wf) ;
Assert.IsTrue(String.Compare(
« Hello\r\nWorld\r\n »,
Écrivain. GetStringBuilder(). ToString()) == 0,
« Chaîne incorrecte écrite »);
}
Figure 7 : Création d’un hello world dans le code
La propriété Text de l’activité WriteLine est une chaîne> InArgument<et, dans cet exemple, j’ai passé une valeur littérale à cette propriété.
L’étape suivante consiste à mettre à jour ce workflow pour utiliser des variables et passer ces variables aux arguments d’activité. La figure 8 montre le nouveau test mis à jour pour utiliser les variables.
[TestMethod]
public void TestHelloWorldVariables()
{
StringWriter writer = new StringWriter();
Console.SetOut(writer);
Séquence wf = nouvelle séquence
{
Variables = {
new Variable<string>{Default = « Hello », Name = « greeting"},
new Variable<string> { Default = « Bill », Name = « name » } },
Activités = {
new WriteLine { Text = new VisualBasicValue<string>(« greeting »),
new WriteLine { Text = new VisualBasicValue<string>(
« name + \"Gates\" »)}
}
};
WorkflowInvoker.Invoke(wf) ;
}
Figure 8 : Coder le flux de travail avec des variables
Dans ce cas, les variables sont définies en tant que type Chaîne> de variable<et reçoivent une valeur par défaut. Les variables sont déclarées dans l’activité Sequence, puis référencées à partir de l’argument Text des deux activités. Sinon, je pourrais utiliser des expressions pour initialiser les variables comme illustré dans la figure 9 où la classe VisualBasicValue<TResult> est utilisée en passant une chaîne représentant l’expression. Dans le premier cas, l’expression fait référence au nom de la variable et, dans le deuxième cas, la variable est concaténée avec une valeur littérale. La syntaxe utilisée dans les expressions textuelles est Visual Basic, même lors de l’écriture de code en C#.
Activités = {
new WriteLine { Text = new VisualBasicValue<string>(« greeting »),
TextWriter = writer },
new WriteLine { Text = new VisualBasicValue<string>(« name + \"Gates\" »),
TextWriter = writer }
}
Figure 9 : Utilisation d’expressions pour définir des arguments
Alors que les exemples de code permettent d’illustrer des points importants et de se sentir généralement à l’aise pour les développeurs, la plupart des utilisateurs utilisent le concepteur pour créer des flux de travail. Dans le concepteur, vous faites glisser des activités sur l’aire de conception et utilisez une grille de propriétés mise à jour mais familière pour définir des arguments. En outre, le concepteur de flux de travail contient des régions extensibles en bas pour modifier les arguments du flux de travail et des variables. Pour créer un flux de travail similaire dans le concepteur, ajoutez un nouveau projet à la solution, en choisissant le modèle Bibliothèque d’activités, puis ajoutez une nouvelle activité.
Lorsque le concepteur de flux de travail s’affiche pour la première fois, il ne contient aucune activité. La première étape de la définition de l’activité consiste à choisir l’activité racine. Pour cet exemple, ajoutez une activité Organigramme au concepteur en la faisant glisser à partir de la catégorie Organigramme dans la boîte à outils. Dans le concepteur d’organigramme, faites glisser deux activités WriteLine à partir de la boîte à outils et ajoutez-les l’une sous l’autre à l’organigramme. Vous devez maintenant connecter les activités ensemble afin que l’organigramme connaisse le chemin à suivre. Pour ce faire, pointez d’abord sur le cercle vert « start » en haut de l’organigramme pour voir les poignées de saisie, puis cliquez et faites-en glisser une sur la première Ligne d’écriture et déposez-la sur la poignée de glissement qui apparaît en haut de l’activité. Procédez de la même façon pour connecter la première ligne d’écriture à la deuxième ligne d’écriture. L’aire de conception doit ressembler à la figure 10.
Figure 10 : Disposition de l’organigramme Hello
Une fois la structure en place, vous devez configurer certaines variables. En cliquant sur l’aire de conception, puis en cliquant sur le bouton Variables en bas du concepteur, vous pouvez modifier la collection de variables pour l’organigramme. Ajoutez deux variables appelées « greeting » et « name » pour répertorier et définissez les valeurs par défaut sur « Hello » et « Bill », respectivement. Veillez à inclure les guillemets lors de la définition des valeurs, car il s’agit d’une expression afin que les chaînes littérales doivent être entre guillemets. La figure 11 montre la fenêtre des variables configurée avec les deux variables et leurs valeurs par défaut.
Figure 11 : Variables définies dans le concepteur de flux de travail
Pour utiliser ces variables dans les activités WriteLine, entrez « greeting » (sans les guillemets) dans la grille des propriétés pour l’argument Text de la première activité et « name » (là encore sans les guillemets) pour l’argument Text sur la deuxième ligne d’écriture. Au moment de l’exécution, lorsque les arguments Text sont évalués, la valeur de la variable est résolue et utilisée par l’activité WriteLine.
Dans le projet de test, ajoutez une référence au projet contenant votre workflow. Vous pouvez ensuite ajouter une méthode de test comme celle illustrée dans la figure 12 pour appeler ce workflow et tester la sortie. Dans la figure 12, vous pouvez voir que le flux de travail est en cours de création en instanciant un instance de la classe créée. Dans ce cas, le workflow a été défini dans le concepteur et compilé dans une classe dérivant de Activity qui peut ensuite être appelée.
[TestMethod]
public void TestHelloFlowChart()
{
StringWriter tWriter = new StringWriter();
Console.SetOut(tWriter);
Workflows.HelloFlow wf = new Workflows.HelloFlow();
WorkflowInvoker.Invoke(wf) ;
Assertions omises
}
Figure 12 : Test de l’organigramme
Jusqu’à présent, j’ai utilisé des variables dans le flux de travail et les ai utilisées pour fournir des valeurs aux arguments sur les activités WriteLine. Le flux de travail peut également avoir des arguments définis qui vous permettent de passer des données dans le flux de travail lors de l’appel de celui-ci et de recevoir une sortie lorsque le flux de travail est terminé. Pour mettre à jour l’organigramme de l’exemple précédent, supprimez la variable « name » (sélectionnez-la et appuyez sur la touche Suppr) et créez à la place un argument « name » de type chaîne. Vous procédez de la même façon, sauf que vous utilisez le bouton Arguments pour afficher l’éditeur d’arguments. Notez que les arguments peuvent également avoir une direction et que vous n’avez pas besoin de fournir une valeur par défaut, car la valeur sera transmise à l’activité au moment de l’exécution. Étant donné que vous utilisez le même nom pour l’argument que pour la variable, les arguments Texte des activités WriteLine restent valides. À présent, au moment de l’exécution, ces arguments évaluent et résolvent la valeur de l’argument « name » sur le flux de travail et utilisent cette valeur. Ajoutez un argument supplémentaire de type string avec la direction Out et le nom « fullGreeting » ; cette valeur est retournée au code appelant.
Figure 13 : Définition d’arguments pour le flux de travail
Dans l’organigramme, ajoutez une activité Assigner et connectez-la à la dernière activité WriteLine. Pour l’argument À, entrez « fullGreeting » (sans guillemets) et pour l’argument Valeur, entrez « greeting & name » (sans guillemets). Cette opération affecte la concaténation de la variable greeting avec l’argument name à l’argument de sortie fullGreeting.
Maintenant, dans le test unitaire, mettez à jour le code pour fournir l’argument lors de l’appel du workflow. Les arguments sont passés au flux de travail sous la forme d’une chaîne de dictionnaire<, objet> où la clé est le nom de l’argument. Pour ce faire, modifiez simplement l’appel pour appeler le flux de travail, comme illustré dans la figure 14. Notez que les arguments de sortie sont également contenus dans une chaîne dictionary<, collection d’objets> clé sur le nom de l’argument.
Chaîne IDictionary<, résultats de l’objet> = WorkflowInvoker.Invoke(wf,
new Dictionary<string,object> {
{"name », « Bill » } }
);
string outValue = results["fullGreeting"]. ToString();
Figure 14 : Passage d’arguments à un flux de travail
Maintenant que vous avez vu les bases de la façon de mettre en place des activités, des variables et des arguments, je vais vous guider dans une visite guidée des activités incluses dans le framework pour permettre des flux de travail plus intéressants axés sur la logique métier au lieu de concepts de bas niveau.
Visite guidée de la palette d’activités de flux de travail
Avec n’importe quel langage de programmation, vous vous attendez à disposer de constructions de base pour définir votre logique d’application. Lors de l’utilisation d’une infrastructure de développement de niveau supérieur comme WF, cette attente ne change pas. Tout comme vous avez des instructions dans des langages .NET tels que If/Else, Switch et While, pour la gestion du flux de contrôle, vous avez également besoin de ces mêmes fonctionnalités lors de la définition de votre logique dans un flux de travail déclaratif. Ces fonctionnalités se présentent sous la forme de la bibliothèque d’activités de base fournie avec l’infrastructure. Dans cette section, je vais vous donner une visite rapide des activités fournies avec le framework pour vous donner une idée des fonctionnalités fournies prêtes à l’emploi.
Primitives d’activité et activités de collection
Lorsque vous passez à un modèle de programmation déclaratif, il est facile de commencer à se demander comment effectuer des tâches courantes de manipulation d’objets qui sont de seconde nature lors de l’écriture de code. Pour les tâches dans lesquelles vous travaillez avec des objets et qui doivent définir des propriétés, appeler des commandes ou gérer une collection d’éléments, il existe un ensemble d’activités conçues spécifiquement avec ces tâches à l’esprit.
Activité | Description |
---|---|
Assigner |
Affecte une valeur à un emplacement en activant les variables de paramètre. |
Retarder |
Retarde le chemin d’exécution pendant une durée spécifiée. |
InvokeMethod |
Appelle une méthode sur un objet .NET ou une méthode statique sur un type .NET, éventuellement avec un type de retour T. |
WriteLine |
Écrit le texte spécifié dans un enregistreur de texte : la valeur par défaut est Console.Out |
AddToCollection<T> |
Ajoute un élément à une collection typée. |
RemoveFromCollection<T> |
Supprime un élément d’une collection typée. |
ClearCollection<T> |
Supprime tous les éléments d’une collection. |
ExistsInCollection<T> |
Retourne une valeur booléenne indiquant si l’élément spécifié existe dans la collection. |
Activités de flux de contrôle
Lors de la définition de la logique métier ou des processus métier, il est essentiel de contrôler le flux d’exécution. Les activités de flux de contrôle incluent des notions de base telles que Sequence, qui fournit un conteneur commun lorsque vous devez exécuter des étapes dans l’ordre, et une logique de branchement commune telle que les activités If et Switch. Les activités de flux de contrôle incluent également la logique de bouclage basée sur les données (ForEach) et conditions(While). Les activités parallèles, qui permettent à plusieurs activités asynchrones d’effectuer le travail en même temps, sont les plus importantes pour simplifier la programmation complexe.
Activité | Description |
---|---|
Séquence |
Pour l’exécution d’activités en série |
While/DoWhile |
Exécute une activité enfant alors qu’une condition (expression) a la valeur true |
ForEach<T> |
Itère sur une collection énumérable et exécute l’activité enfant une fois pour chaque élément de la collection, en attendant que l’enfant se termine avant de commencer l’itération suivante. Fournit un accès typé à l’élément individuel qui pilote l’itération sous la forme d’un argument nommé. |
Si |
Exécute l’une des deux activités enfants en fonction du résultat de la condition (expression). |
Commutateur<T> |
Évalue une expression et planifie l’activité enfant avec une clé correspondante. |
Parallèle |
Planifie toutes les activités enfants à la fois, mais fournit également une condition d’achèvement pour permettre à l’activité d’annuler toutes les activités enfants en attente si certaines conditions sont remplies. |
ParallelForEach<T> |
Itère sur une collection énumérable et exécute l’activité enfant une fois pour chaque élément de la collection, en planifiant toutes les instances en même temps. À l’instar de ForEach<T>, cette activité fournit l’accès à l’élément de données actuel sous la forme d’un argument nommé. |
Choisir |
Planifie toutes les activités PickBranch enfants et annule toutes les activités, sauf la première, pour que son déclencheur soit terminé. L’activité PickBranch a à la fois un déclencheur et une action ; chacun est une activité. Lorsqu’une activité de déclencheur se termine, pick annule toutes ses autres activités enfants. |
Les deux exemples ci-dessous montrent plusieurs de ces activités utilisées pour illustrer comment composer ces activités ensemble. Le premier exemple, figure 15, inclut l’utilisation de ParallelForEach<T> pour utiliser une liste d’URL et obtenir de façon asynchrone le flux RSS à l’adresse spécifiée. Une fois le flux retourné, le T> ForEach<est utilisé pour itérer sur les éléments de flux et les traiter.
Notez que le code déclare et définit un instance VisualBasicSettings avec des références aux types System.ServiceModel.Syndication. Cet objet settings est ensuite utilisé lors de la déclaration d’instances VisualBasicValue<T> référençant des types de variables à partir de cet espace de noms afin d’activer la résolution de type pour ces expressions. Notez également que le corps de l’activité ParallelForEach prend une ActivityAction mentionnée dans la section sur la création d’activités personnalisées. Ces actions utilisent DelegateInArgument et DelegateOutArgument de la même façon que les activités utilisent InArgument et OutArgument.
Figure 15 : Activités de flux de contrôle
Le deuxième exemple, figure 16, est un modèle courant d’attente d’une réponse avec un délai d’expiration. Par exemple, attendre qu’un responsable approuve une demande et envoyer un rappel si la réponse n’est pas arrivée dans le délai imparti. Une activité DoWhile provoque la répétition de l’attente d’une réponse alors que l’activité Pick est utilisée pour exécuter à la fois une activité ManagerResponse et une activité Delay en même temps que des déclencheurs. Lorsque le délai se termine en premier, le rappel est envoyé et lorsque l’activité ManagerResponse se termine en premier, l’indicateur est défini pour sortir de la boucle DoWhile.
Figure 16 : Activités Pick et DoWhile
L’exemple de la figure 16 montre également comment les signets peuvent être utilisés dans les workflows. Un signet est créé par une activité pour marquer une place dans le flux de travail, afin que le traitement puisse reprendre à partir de ce point à une date ultérieure. L’activité Delay a un signet qui est repris une fois qu’un certain laps de temps est écoulé. L’activité ManagerResponse est une activité personnalisée qui attend l’entrée et reprend le flux de travail une fois les données arrivées. Les activités de messagerie, abordées sous peu, sont les activités main pour l’exécution des signets. Lorsqu’un workflow ne traite pas activement le travail, lorsqu’il n’attend que la reprise des signets, il est considéré comme inactif et peut être conservé dans un magasin durable. Les signets seront abordés plus en détail dans la section sur la création d’activités personnalisées.
Migration
Pour les développeurs qui utilisent WF3, l’activité d’interopérabilité peut jouer un rôle essentiel dans la réutilisation des ressources existantes. L’activité, qui se trouve dans l’assembly System.Workflow.Runtime, encapsule votre type d’activité existant et expose les propriétés de l’activité sous forme d’arguments dans le modèle WF4. Étant donné que les propriétés sont des arguments, vous pouvez utiliser des expressions pour définir les valeurs. La figure 17 montre la configuration de l’activité Interop pour appeler une activité WF3. Les arguments d’entrée sont définis avec des références à des variables dans l’étendue dans la définition de workflow. Les activités intégrées à WF3 avaient des propriétés au lieu d’arguments, de sorte que chaque propriété reçoit un argument d’entrée et de sortie correspondant, ce qui vous permet de différencier les données que vous envoyez dans l’activité et les données que vous prévoyez de récupérer après l’exécution de l’activité. Dans un nouveau projet WF4, vous ne trouverez pas cette activité dans la boîte à outils, car l’infrastructure cible est définie sur .NET Framework 4 Client Profile. Remplacez l’infrastructure cible dans les propriétés du projet par .NET Framework 4, et l’activité s’affiche dans la boîte à outils.
Figure 17 : Configuration de l’activité d’interopérabilité
Organigramme
Lors de la conception des flux de travail d’organigramme, plusieurs constructions peuvent être utilisées pour gérer le flux d’exécution dans l’organigramme. Ces constructions fournissent elles-mêmes des étapes simples, des points de décision simples basés sur une seule condition ou une instruction switch. La véritable puissance de l’organigramme est la possibilité de connecter ces types de nœuds au flux souhaité.
Construction/activité | Description |
---|---|
Organigramme |
Conteneur d’une série d’étapes de flux, chaque étape de flux peut être n’importe quelle activité ou l’une des constructions suivantes, mais pour être exécutée, elle doit être connectée dans l’organigramme. |
FlowDecision |
Fournit une logique de branchement basée sur une condition. |
FlowSwitch<T> |
Active plusieurs branches en fonction de la valeur d’une expression. |
FlowStep |
Représente une étape de l’organigramme avec la possibilité d’être connecté à d’autres étapes. Ce type n’est pas affiché dans la boîte à outils, car il est implicitement ajouté par le concepteur. Cette activité encapsule les autres activités dans le flux de travail et fournit la sémantique de navigation pour la ou les étapes suivantes du workflow. |
Il est important de noter que même s’il existe des activités spécifiques pour le modèle Organigramme, toutes les autres activités peuvent être utilisées dans le flux de travail. De même, une activité d’organigramme peut être ajoutée à une autre activité pour fournir l’exécution et la sémantique de conception d’un organigramme, au sein de ce flux de travail. Cela signifie que vous pouvez avoir une séquence avec plusieurs activités et un organigramme au milieu.
Activités de messagerie
L’un des principaux axes de WF4 est l’intégration plus étroite entre WF et WCF. En termes de flux de travail, cela signifie des activités de modélisation des opérations de messagerie telles que l’envoi et la réception de messages. Il existe en fait plusieurs activités différentes incluses dans l’infrastructure pour la messagerie, chacune avec des fonctionnalités et des objectifs légèrement différents.
Activité | Description |
---|---|
Envoyer/recevoir |
Activités de messagerie unidirectionnelle pour envoyer ou recevoir un message. Ces mêmes activités sont composées en interactions de style requête/réponse. |
ReceiveAndSendReply |
Modélise une opération de service qui reçoit un message et renvoie une réponse. |
SendAndReceiveReply |
Appelle une opération de service et reçoit la réponse. |
InitializeCorrelation |
Autorisez l’initialisation explicite des valeurs de corrélation dans le cadre de la logique de flux de travail, plutôt que d’extraire les valeurs d’un message. |
CorrelationScope |
Définit une étendue d’exécution où un handle de corrélation est accessible pour recevoir et envoyer des activités, ce qui simplifie la configuration d’un handle partagé par plusieurs activités de messagerie. |
TransactedReceiveScope |
Permet d’inclure la logique de flux de travail dans la même transaction transmise dans une opération WCF à l’aide de l’activité Receive. |
Pour appeler une opération de service à partir d’un flux de travail, vous allez suivre les étapes familières de l’ajout d’une référence de service à votre projet de workflow. Le système de projet dans Visual Studio consomme ensuite les métadonnées du service et crée une activité personnalisée pour chaque opération de service trouvée dans le contrat. Vous pouvez considérer cela comme l’équivalent du flux de travail d’un proxy WCF qui serait créé dans un projet C# ou Visual Basic. Par exemple, en prenant un service existant qui recherche des réservations d’hôtel avec le contrat de service illustré dans la figure 18 ; l’ajout d’une référence de service à celle-ci génère une activité personnalisée illustrée dans la figure 19.
[ServiceContract]
interface publique IHotelService
{
[OperationContract]
Liste HotelSearchResult<> SearchHotels(
HotelSearchRequest requestDetails);
}
Figure 18 : Contrat de service
Figure 19 : activité WCF personnalisée
Pour plus d’informations sur la création de workflows exposés en tant que services WCF, consultez la section Services de flux de travail plus loin dans ce document.
Transactions et gestion des erreurs
L’écriture de systèmes fiables peut être difficile et nécessite une bonne gestion des erreurs et la gestion pour maintenir un état cohérent dans votre application. Pour un workflow, les étendues de gestion de la gestion des exceptions et des transactions sont modélisées à l’aide d’activités de type ActivityAction ou Activity pour permettre aux développeurs de modéliser la logique de traitement des erreurs. Au-delà de ces modèles courants de cohérence, WF4 inclut également des activités qui vous permettent de modéliser la coordination distribuée à long terme par le biais de la compensation et de la confirmation.
Activité | Description |
---|---|
CancellationScope |
Utilisé pour permettre au développeur de workflow de réagir en cas d’annulation d’un travail. |
TransactionScope |
Active la sémantique similaire à l’utilisation d’une étendue de transaction dans le code en exécutant toutes les activités enfants dans l’étendue d’une transaction. |
TryCatch/Catch<T> |
Utilisé pour modéliser la gestion des exceptions et intercepter les exceptions typées. |
Throw |
Peut être utilisé pour lever une exception de l’activité. |
Rethrow |
Utilisé pour recréer une exception, généralement une exception qui a été interceptée à l’aide de l’activité TryCatch. |
CompensableActivity |
Définit la logique d’exécution d’activités enfants dont les actions peuvent avoir besoin d’être compensées après la réussite. Fournit un espace réservé pour la logique de compensation, la logique de confirmation et la gestion des annulations. |
Compensate |
Appelle la logique de gestion des compensations pour une activité pouvant faire l’objet d’une indemnisation. |
Confirmer |
Appelle la logique de confirmation pour une activité pouvant faire l’objet d’une indemnisation. |
L’activité TryCatch fournit un moyen familier d’étendre un ensemble de travail pour intercepter toutes les exceptions qui peuvent se produire, et l’activité Catch<T> fournit le conteneur pour la logique de gestion des exceptions. Vous pouvez voir un exemple d’utilisation de cette activité dans la figure 20, chaque bloc d’exception typé étant défini par une activité Catch<T> , ce qui permet d’accéder à l’exception via l’argument nommé affiché à gauche de chaque capture. Si le besoin s’en fait sentir, l’activité Rethrow peut être utilisée pour réactiver l’exception interceptée, ou l’activité Throw pour lever une nouvelle exception de votre propre.
Figure 20 : activité TryCatch
Les transactions permettent d’assurer la cohérence dans le travail de courte durée et l’activité TransactionScope fournit le même type d’étendue que vous pouvez obtenir dans le code .NET à l’aide de la classe TransactionScope.
Enfin, lorsque vous devez maintenir la cohérence, mais que vous ne pouvez pas utiliser une transaction atomique entre les ressources, vous pouvez utiliser la compensation. La rémunération vous permet de définir un ensemble de travail qui, s’il se termine, peut avoir un ensemble d’activités pour compenser les modifications apportées. Par exemple, considérez l’activité d’indemnisation de la figure 21 où un e-mail est envoyé. Si une fois l’activité terminée, une exception se produit, la logique de compensation peut être appelée pour ramener le système à un état cohérent ; dans ce cas, un e-mail de suivi pour retirer le message précédent.
Figure 21 : Activité pouvant être compensée
Création et exécution de workflows
Comme avec n’importe quel langage de programmation, il existe deux choses fondamentales que vous effectuez avec les flux de travail : les définir et les exécuter. Dans les deux cas, WF vous offre plusieurs options pour vous offrir flexibilité et contrôle.
Options de conception de flux de travail
Lors de la conception ou de la définition de workflows, il existe deux options main : code ou XAML. XAML fournit une expérience véritablement déclarative et permet de définir toute la définition de votre flux de travail dans le balisage XML, en référençant les activités et les types créés à l’aide de .NET. La plupart des développeurs utiliseront probablement le concepteur de flux de travail pour créer des flux de travail qui aboutiront à une définition de flux de travail XAML déclarative. Étant donné que XAML n’est que du XML, cependant, n’importe quel outil peut être utilisé pour le créer, ce qui est l’une des raisons pour lesquelles il s’agit d’un modèle si puissant pour la création d’applications. Par exemple, le CODE XAML illustré dans la figure 22 a été créé dans un éditeur de texte simple et peut être compilé ou utilisé directement pour exécuter une instance du flux de travail en cours de définition.
<p:Activity x:Class="Workflows.HelloSeq » xmlns= »https://schemas.microsoft.com/netfx/2009/xaml/activities/design" xmlns:p= »https://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:x= »https://schemas.microsoft.com/winfx/2006/xaml">
<x:Members>
<x:Property Name="greeting » Type="p:InArgument(x:String) » />
<x:Property Name="name » Type="p:InArgument(x:String) » />
</x:Members>
<p:Sequence>
<p:WriteLine>[greeting & « from workflow"]</p:WriteLine>
<p:WriteLine>[name]</p:WriteLine>
</p:Sequence>
</p:Activity>
Figure 22 : Flux de travail défini en XAML
Ce même CODE XAML est généré par le concepteur et peut être généré par des outils personnalisés, ce qui le rend beaucoup plus facile à gérer. En outre, il est également possible, comme indiqué précédemment, de créer des flux de travail à l’aide de code. Cela implique la création de la hiérarchie d’activités par le biais de l’utilisation des différentes activités dans l’infrastructure et de vos activités personnalisées. Vous avez déjà vu plusieurs exemples de workflows définis entièrement dans le code. Contrairement à WF3, il est désormais possible de créer un workflow dans le code et de le sérialiser facilement en XAML ; offrant plus de flexibilité dans la modélisation et la gestion des définitions de flux de travail.
Comme je vais le montrer, pour exécuter un flux de travail, il vous suffit d’une activité qui peut être un instance code intégré ou un code créé à partir de XAML. Le résultat final de chacune des techniques de modélisation est soit une classe dérivant de Activity, soit une représentation XML qui peut être désérialisée ou compilée dans une activité.
Options d’exécution des flux de travail
Pour exécuter un flux de travail, vous avez besoin d’une activité qui définit le flux de travail. Il existe deux façons courantes d’obtenir une activité qui peut être exécutée : la créer dans du code ou la lire dans un fichier XAML et désérialiser le contenu dans une activité. La première option est simple et j’ai déjà montré plusieurs exemples. Pour charger un fichier XAML, vous devez utiliser la classe ActivityXamlServices qui fournit une méthode Load statique. Transmettez simplement un objet Stream ou XamlReader et vous récupérez l’activité représentée dans le XAML.
Une fois que vous avez une activité, le moyen le plus simple de l’exécuter consiste à utiliser la classe WorkflowInvoker comme je l’ai fait dans les tests unitaires précédemment. La méthode Invoke de cette classe a un paramètre de type Activity et retourne une chaîne IDictionary<, objet>. Si vous devez transmettre des arguments dans le flux de travail, vous les définissez d’abord sur le flux de travail, puis transmettez les valeurs avec l’activité dans la méthode Invoke en tant que dictionnaire de paires nom/valeur. De même, tous les arguments Out ou In/Out définis sur le flux de travail seront retournés à la suite de l’exécution de la méthode. La figure 23 fournit un exemple de chargement d’un flux de travail à partir d’un fichier XAML, de passage d’arguments dans le flux de travail et de récupération des arguments de sortie résultants.
Activité mathWF ;
using (Stream mathXaml = File.OpenRead(« Math.xaml »))
{
mathWF = ActivityXamlServices.Load(mathXaml);
}
var outputs = WorkflowInvoker.Invoke(mathWF,
nouvelle chaîne de dictionnaire<, objet> {
{ « operand1 », 5 },
{ « operand2 », 10 },
{ « operation », « add » } });
Assert.AreEqual<int>(15, (int)outputs["result"], « Résultat incorrect retourné »);
Figure 23 : Appel d’un flux de travail XAML libre avec des arguments d’entrée et de sortie
Notez dans cet exemple que l’activité est chargée à partir d’un fichier XAML et qu’elle peut toujours accepter et retourner des arguments. Le flux de travail a été développé à l’aide du concepteur dans Visual Studio pour plus de facilité, mais il peut être développé dans un concepteur personnalisé et stocké n’importe où. Le CODE XAML peut être chargé à partir d’une base de données, d’une bibliothèque SharePoint ou d’un autre magasin avant d’être remis au runtime pour exécution.
L’utilisation de WorkflowInvoker fournit le mécanisme le plus simple pour exécuter des flux de travail de courte durée. Il fait essentiellement que le flux de travail agit comme un appel de méthode dans votre application, ce qui vous permet de tirer plus facilement parti de tous les avantages de WF sans avoir à effectuer beaucoup de travail pour héberger WF lui-même. Cela est particulièrement utile lors du test unitaire de vos activités et flux de travail, car il réduit la configuration de test nécessaire pour exercer un composant en cours de test.
Une autre classe d’hébergement courante est workflowApplication qui fournit un handle sécurisé à un flux de travail qui s’exécute dans le runtime et vous permet de gérer plus facilement des flux de travail de longue durée. Avec WorkflowApplication, vous pouvez toujours passer des arguments dans le flux de travail de la même manière qu’avec WorkflowInvoker, mais vous utilisez la méthode Run pour démarrer le flux de travail en cours d’exécution. À ce stade, le workflow commence à s’exécuter sur un autre thread et le contrôle retourne au code appelant.
Étant donné que le flux de travail est maintenant en cours d’exécution asynchrone, dans votre code d’hébergement, vous voudrez probablement savoir quand le flux de travail se termine, ou s’il lève une exception, etc. Pour ces types de notifications, la classe WorkflowApplication a un ensemble de propriétés de type Action<T> qui peuvent être utilisées comme des événements pour ajouter du code afin de réagir à certaines conditions d’exécution du flux de travail, notamment : exception abandonnée, non prise en charge, terminée, inactive et déchargée. Lorsque vous exécutez un flux de travail à l’aide de WorkflowApplication, vous pouvez utiliser du code similaire à celui illustré dans la figure 24 à l’aide d’actions pour gérer les événements.
WorkflowApplication wf = new WorkflowApplication(new Flowchart1());
Wf. Terminé = délégué(WorkflowApplicationCompletedEventArgs e)
{
Console.WriteLine(« Workflow {0} complete », e.InstanceId);
};
Wf. Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
{
Console.WriteLine(e.Reason) ;
};
Wf. OnUnhandledException =
delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Console.WriteLine(e.UnhandledException.ToString());
retourne UnhandledExceptionAction.Terminate ;
};
Wf. Run();
Figure 24 : Actions workflowApplication
Dans cet exemple, l’objectif du code hôte, une application console simple, est d’avertir l’utilisateur via la console lorsque le flux de travail se termine ou si une erreur se produit. Dans un système réel, l’hôte s’intéressera à ces événements et y réagira probablement différemment pour fournir aux administrateurs des informations sur les défaillances ou pour mieux gérer les instances.
Extensions de flux de travail
L’une des principales fonctionnalités de WF, depuis WF3, est qu’il est suffisamment léger pour être hébergé dans n’importe quel domaine d’application .NET. Étant donné que le runtime peut s’exécuter dans différents domaines et qu’il peut avoir besoin d’une sémantique d’exécution personnalisée, différents aspects des comportements du runtime doivent être externalisés à partir du runtime. C’est là que les extensions de workflow entrent en jeu. Les extensions de flux de travail vous permettent, en tant que développeur écrivant le code hôte, si vous le souhaitez, d’ajouter un comportement au runtime en l’étendant avec du code personnalisé.
Le runtime WF connaît deux types d’extensions : les extensions de persistance et de suivi. L’extension de persistance fournit les fonctionnalités principales permettant d’enregistrer l’état d’un flux de travail dans un magasin durable et de récupérer cet état lorsque cela est nécessaire. L’expédition de l’extension de persistance avec l’infrastructure inclut la prise en charge de Microsoft SQL Server, mais les extensions peuvent être écrites pour prendre en charge d’autres systèmes de base de données ou magasins durables.
Persistance
Pour utiliser l’extension de persistance, vous devez d’abord configurer une base de données pour contenir l’état, ce qui peut être fait à l’aide des scripts SQL trouvés dans %windir%\Microsoft.NET\Framework\v4.0.30319\sql\<language> (par exemple, c:\windows\microsoft.net\framework\v4.0.30319\sql\en\). Après avoir créé une base de données, vous exécutez les deux scripts pour créer à la fois la structure (SqlWorkflowInstanceStoreSchema.sql) et les procédures stockées (SqlWorkflowInstanceStoreLogic.sql) dans la base de données.
Une fois la base de données configurée, vous utilisez SqlWorkflowInstanceStore avec la classe WorkflowApplication. Vous créez d’abord le magasin et le configurez, puis vous le fournissez à WorkflowApplication, comme illustré dans la figure 25.
Application WorkflowApplication = new WorkflowApplication(activity) ;
InstanceStore instanceStore = new SqlWorkflowInstanceStore(
@"Data Source=.\\SQLEXPRESS;Integrated Security=True");
Vue InstanceView = instanceStore.Execute(
instanceStore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(),
TimeSpan.FromSeconds(30));
instanceStore.DefaultInstanceOwner = vue. InstanceOwner ;
Application. InstanceStore = instanceStore ;
Figure 25 : Ajout du fournisseur de persistance SQL
Il existe deux façons dont le flux de travail peut être conservé. La première consiste à utiliser directement l’activité Persist dans un workflow. Lorsque cette activité s’exécute, l’état du flux de travail est conservé dans la base de données, ce qui enregistre l’état actuel du flux de travail. Cela permet à l’auteur du flux de travail de contrôler quand il est important d’enregistrer l’état actuel du flux de travail. La deuxième option permet à l’application d’hébergement de conserver l’état du flux de travail lorsque différents événements se produisent sur le instance de flux de travail; très probablement lorsque le flux de travail est inactif. En vous inscrivant à l’action PersistableIdle sur workflowApplication, votre code hôte peut ensuite répondre à l’événement pour indiquer si le instance doit être conservé, déchargé ou non. La figure 26 montre qu’une application hôte s’inscrit pour recevoir une notification lorsque le flux de travail est inactif et qu’il est persistant.
Wf. PersistableIdle = (waie) => PersistableIdleAction.Persist;
Figure 26 : Déchargement du flux de travail en cas d’inactivité
Une fois qu’un flux de travail a été inactif et conservé, le runtime WF peut le décharger de la mémoire, ce qui libère des ressources pour d’autres flux de travail qui doivent être traités. Vous pouvez choisir de charger votre flux de travail instance en retournant l’énumération appropriée à partir de l’action PersistableIdle. Une fois qu’un flux de travail est déchargé, à un moment donné, l’hôte souhaite le recharger hors du magasin de persistance pour le reprendre. Pour ce faire, le flux de travail instance doit être chargé à l’aide d’un magasin de instance et de l’identificateur instance. À son tour, le magasin instance est invité à charger l’état. Une fois l’état chargé, la méthode Run sur workflowApplication peut être utilisée ou un signet peut être repris comme illustré dans la figure 27. Pour plus d’informations sur les signets, consultez la section Activités personnalisées.
Application WorkflowApplication = new WorkflowApplication(activity) ;
Application. InstanceStore = instanceStore ;
Application. Load(id) ;
Application. ResumeBookmark(readLineBookmark, entrée) ;
Figure 27 : Reprise d’un flux de travail persistant
L’une des modifications apportées à WF4 est la façon dont l’état des flux de travail est conservé et s’appuie fortement sur les nouvelles techniques de gestion des données des arguments et des variables. Au lieu de sérialiser l’arborescence d’activité entière et de conserver l’état pendant la durée de vie du flux de travail, seuls les variables d’étendue et les valeurs d’argument, ainsi que certaines données d’exécution telles que les informations de signet, sont conservées. Notez dans la figure 27 que lorsque le instance persistant est rechargé, en plus d’utiliser le magasin instance, l’activité qui définit le flux de travail est également transmise. Essentiellement, l’état de la base de données est appliqué à l’activité fournie. Cette technique offre une plus grande flexibilité pour le contrôle de version et offre de meilleures performances, car l’état a un encombrement mémoire plus faible.
Suivi
La persistance permet à l’hôte de prendre en charge des processus à long terme, des instances d’équilibrage de charge entre les hôtes et d’autres comportements tolérants aux pannes. Toutefois, une fois le workflow instance terminé, l’état de la base de données est souvent supprimé, car il n’est plus utile. Dans un environnement de production, il est essentiel de disposer d’informations sur ce qu’un workflow fait actuellement et ce qu’il a fait pour gérer les flux de travail et obtenir des informations sur le processus métier. La possibilité de suivre ce qui se passe dans votre application est l’une des fonctionnalités attrayantes de l’utilisation du runtime WF.
Le suivi se compose de deux composants principaux : le suivi des participants et les profils de suivi. Un profil de suivi définit les événements et les données que vous souhaitez que le runtime suive. Les profils peuvent inclure trois principaux types de requêtes :
- ActivityStateQuery : permet d’identifier les états d’activité (par exemple, fermés) et les variables ou arguments pour extraire des données
- WorkflowInstanceQuery : utilisé pour identifier les événements de flux de travail
- CustomTrackingQuery : permet d’identifier des appels explicites pour suivre les données, généralement au sein d’activités personnalisées
À titre d’exemple, la Figure 28 montre un TrackingProfile en cours de création qui inclut un ActivityStateQuery et un WorkflowInstanceQuery. Notez que les requêtes indiquent à la fois quand collecter des informations et quelles données collecter. Pour l’ActivityStateQuery, j’ai inclus une liste de variables dont la valeur doit être extraite et ajoutée aux données suivies.
Profil TrackingProfile = new TrackingProfile
{
Name = « SimpleProfile »,
Requêtes = {
new WorkflowInstanceQuery {
States = { « * » }
},
new ActivityStateQuery {
ActivityName = « WriteLine »,
States={ « * » },
Variables = {"Text » }
}
}
};
Figure 28 : Création d’un profil de suivi
Un participant de suivi est une extension qui peut être ajoutée au runtime et est responsable du traitement des enregistrements de suivi au fur et à mesure qu’ils sont émis. La classe de base TrackingParticipant définit une propriété pour fournir un TrackingProfile et une méthode Track qui gère le suivi. La figure 29 montre un participant de suivi personnalisé limité qui écrit des données dans la console. Pour utiliser le participant de suivi, il doit être initialisé avec un profil de suivi, puis ajouté à la collection d’extensions sur le flux de travail instance.
public class ConsoleTrackingParticipant : TrackingParticipant
{
protected override void Track(TrackingRecord record, TimeSpan timeout)
{
ActivityStateRecord aRecord = enregistrer comme ActivityStateRecord ;
if (aRecord != null)
{
Console.WriteLine( »{0} état entré {1}« ,
aRecord.Activity.Name, aRecord.State) ;
foreach (élément var dans aRecord.Arguments)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(« Variable :{0} a la valeur : {1}« ,
Article. Clé, élément. Valeur) ;
Console.ResetColor();
}
}
}
}
Figure 29 : Participant au suivi de la console
WF est fourni avec un EtwTrackingParticipant (ETW = Trace d’entreprise pour Windows) que vous pouvez ajouter au runtime pour activer le suivi haute performance des données. ETW est un système de suivi qui est un composant natif dans Windows et est utilisé par de nombreux composants et services du système d’exploitation, y compris les pilotes et d’autres codes au niveau du noyau. Les données écrites dans ETW peuvent être consommées à l’aide de code personnalisé ou d’outils tels que le prochain Windows Server AppFabric. Pour les utilisateurs qui migrent à partir de WF3, il s’agit d’un changement, car il n’y aura pas d’expédition de participants de suivi SQL dans le cadre de l’infrastructure. Toutefois, Windows Server AppFabric sera livré avec des consommateurs ETW qui collecteront les données ETW et les stockeront dans une base de données SQL. De même, vous pouvez créer un consommateur ETW et stocker les données dans le format de votre choix, ou écrire votre propre participant au suivi, selon ce qui est le plus logique pour votre environnement.
Création d’activités personnalisées
Bien que la bibliothèque d’activités de base inclut une palette complète d’activités permettant d’interagir avec les services, les objets et les collections, elle ne fournit pas d’activités permettant d’interagir avec des sous-systèmes tels que des bases de données, des serveurs de messagerie ou vos objets et systèmes de domaine personnalisés. Une partie de la prise en main de WF4 consistera à déterminer les activités de base dont vous pourriez avoir besoin ou vouloir lors de la création de flux de travail. L’avantage est que lorsque vous créez des unités de travail de base, vous pouvez les combiner en activités plus grossières que les développeurs peuvent utiliser dans leurs flux de travail.
Hiérarchie des classes d’activité
Bien qu’une activité soit une activité soit une activité, il n’est pas vrai que toutes les activités ont les mêmes exigences ou besoins. Pour cette raison, au lieu de toutes les activités qui dérivent directement de l’activité, il existe une hiérarchie de classes de base d’activité, illustrées dans la figure 30, que vous pouvez choisir lors de la création d’une activité personnalisée.
Figure 30 : Hiérarchie des classes d’activité
Pour la plupart des activités personnalisées, vous dérivez d’AsyncCodeActivity, CodeActivity ou NativeActivity (ou de l’une des variantes génériques), ou modélisez votre activité de manière déclarative. À un niveau élevé, les quatre classes de base peuvent être décrites comme suit :
- Activité : permet de modéliser des activités en composant d’autres activités, généralement définies à l’aide de XAML.
- CodeActivity : classe de base simplifiée lorsque vous devez écrire du code pour effectuer le travail.
- AsyncCodeActivity : utilisé lorsque votre activité effectue un travail asynchrone.
- NativeActivity : lorsque votre activité a besoin d’accéder aux éléments internes du runtime, par exemple pour planifier d’autres activités ou créer des signets.
Dans les sections suivantes, je vais créer plusieurs activités pour voir comment utiliser chacune de ces classes de base. En général, lorsque vous réfléchissez à la classe de base à utiliser, vous devez commencer par la classe de base Activity et voir si vous pouvez générer votre activité à l’aide de la logique déclarative, comme indiqué dans la section suivante. Ensuite, codeActivity fournit un modèle simplifié si vous déterminez que vous devez écrire du code .NET pour accomplir votre tâche et AsyncCodeActivity si vous souhaitez que votre activité s’exécute de manière asynchrone. Enfin, si vous écrivez des activités de flux de contrôle comme celles trouvées dans l’infrastructure (par exemple, While, Switch, If), vous devez dériver de la classe de base NativeActivity afin de gérer les activités enfants.
Composition d’activités à l’aide du Concepteur d’activités
Lorsque vous créez un projet de bibliothèque d’activités ou que vous ajoutez un nouvel élément à un projet WF et sélectionnez le modèle d’activité, vous obtenez un fichier XAML contenant un élément Activity vide. Dans le concepteur, cela se présente comme une aire de conception où vous pouvez créer le corps de l’activité. Pour commencer avec une activité qui comportera plusieurs étapes, il est généralement utile de faire glisser une activité De séquence dans le corps, puis de la remplir avec votre logique d’activité réelle, car le corps représente une seule activité enfant.
Vous pouvez considérer l’activité comme vous le feriez pour une méthode sur un composant avec des arguments. Sur l’activité elle-même, vous pouvez définir des arguments, ainsi que leur directionnalité, pour définir l’interface de l’activité. Les variables que vous souhaitez utiliser dans l’activité devront être définies dans les activités qui composent le corps, telles que la séquence racine que j’ai mentionnée précédemment. Par exemple, vous pouvez créer une activité NotifyManager qui compose deux activités plus simples : GetManager et SendMail.
Tout d’abord, créez un projet ActivityLibrary dans Visual Studio 2010, puis renommez le fichier Activity1.xaml en NotifyManager.xaml. Ensuite, faites glisser une activité Sequence à partir de la boîte à outils et ajoutez-la au concepteur. Une fois la séquence en place, vous pouvez la remplir avec des activités enfants, comme illustré dans la figure 31. (Notez que les activités utilisées dans cet exemple sont des activités personnalisées dans un projet référencé et non disponibles dans l’infrastructure.)
Figure 31 : Activité de notification du gestionnaire
Cette activité doit prendre des arguments pour le nom de l’employé et le corps du message. L’activité GetManager recherche le responsable de l’employé et fournit l’e-mail sous forme de chaîne> OutArgument<. Enfin, l’activité SendMail envoie un message au responsable. L’étape suivante consiste à définir les arguments de l’activité en développant la fenêtre Arguments en bas du concepteur et en entrant les informations pour les deux arguments d’entrée requis.
Pour composer ces éléments, vous devez être en mesure de transmettre les arguments d’entrée spécifiés sur l’activité NotifyManager aux activités enfants individuelles. Pour l’activité GetManager, vous avez besoin du nom d’employé qui est un argument dans sur l’activité. Vous pouvez utiliser le nom de l’argument dans la boîte de dialogue de propriété pour l’argument employeeName sur l’activité GetManager, comme illustré dans la figure 31. L’activité SendMail a besoin de l’adresse e-mail du responsable et du message. Pour le message, vous pouvez entrer le nom de l’argument contenant le message. Toutefois, pour l’adresse e-mail, vous avez besoin d’un moyen de transmettre l’argument de sortie de l’activité GetManager à l’argument in pour l’activité SendMail. Pour cela, vous avez besoin d’une variable.
Après avoir mis en surbrillance l’activité Sequence, vous pouvez utiliser la fenêtre Variables pour définir une variable nommée « mgrEmail ». Vous pouvez maintenant entrer ce nom de variable à la fois pour l’argument ManagerEmail out sur l’activité GetManager et l’argument À dans sur l’activité SendMail. Lorsque l’activité GetManager s’exécute, les données de sortie sont stockées dans cette variable et, lorsque l’activité SendMail s’exécute, elle lit les données de cette variable en tant qu’argument in.
L’approche qui vient d’être décrite est un modèle purement déclaratif pour définir le corps d’activité. Dans certains cas, vous pouvez préférer spécifier le corps de l’activité dans le code. Par exemple, votre activité peut inclure une collection de propriétés qui à leur tour conduisent un ensemble d’activités enfants ; un ensemble de valeurs nommées qui pilotent la création d’un ensemble d’activités Assigner serait un cas où l’utilisation de code serait préférable. Dans ce cas, vous pouvez écrire une classe qui dérive de l’activité et écrire du code dans la propriété Implementation pour créer une activité (et tous les éléments enfants) pour représenter les fonctionnalités de votre activité. Le résultat final est le même dans les deux cas : votre logique est définie en composant d’autres activités, seul le mécanisme par lequel le corps est défini est différent. La figure 32 montre la même activité NotifyManager définie dans le code.
classe publique NotifyManager : Activité
{
chaîne> InArgument<public EmployeeName { get; set; }
public InArgument<string> Message { get; set; }
implémentation de l’activité> func<de remplacement protégée
{
get
{
return () =>
{
Variable<string> mgrEmail =
new Variable<string> { Name = « mgrEmail » };
Séquence s = nouvelle séquence
{
Variables = { mgrEmail },
Activités = {
new GetManager {
EmployeeName =
nouvelle chaîne> VisualBasicValue<(« EmployeeName »),
Résultat = mgrEmail,
},
new SendMail {
ToAddress = mgrEmail,
MailBody = nouvelle chaîne> VisualBasicValue<(« Message »),
À partir de = « test@contoso.com »,
Subject = « Email automatisé »
}
}
};
retourner s;
};
}
set { base. Implémentation = valeur ; }
}
}
Figure 32 : Composition de l’activité à l’aide de code
Notez que la classe de base est Activity et que la propriété Implementation est une activité> Func<pour renvoyer une seule activité. Ce code est très similaire à celui présenté précédemment lors de la création de flux de travail, et cela ne devrait pas être surprenant, car l’objectif dans les deux cas est de créer une activité. En outre, dans l’approche du code, les arguments peuvent être définis avec des variables, ou vous pouvez utiliser des expressions pour connecter un argument à un autre, comme indiqué pour les arguments EmployeeName et Message, car ils sont utilisés sur les deux activités enfants.
Écriture de classes d’activité personnalisées
Si vous déterminez que votre logique d’activité ne peut pas être accomplie en composant d’autres activités, vous pouvez écrire une activité basée sur du code. Lorsque vous écrivez des activités dans du code, vous dérivez de la classe appropriée, définissez des arguments, puis remplacez la méthode Execute. La méthode Execute est appelée par le runtime lorsqu’il est temps pour l’activité d’effectuer son travail.
Pour générer l’activité SendMail utilisée dans l’exemple précédent, je dois d’abord choisir le type de base. S’il est possible de créer les fonctionnalités de l’activité SendMail à l’aide de la classe de base Activity et de composer des activités TryCatch<T> et InvokeMethod, pour la plupart des développeurs, il est plus naturel de choisir entre les classes de base CodeActivity et NativeActivity et d’écrire du code pour la logique d’exécution. Étant donné que cette activité n’a pas besoin de créer des signets ou de planifier d’autres activités, je peux dériver de la classe de base CodeActivity. En outre, étant donné que cette activité retourne un seul argument de sortie, elle doit dériver de CodeActivity<TResult> qui fournit un TResult> OutArgument<. La première étape consiste à définir plusieurs arguments d’entrée pour les propriétés d’e-mail. Vous remplacez ensuite la méthode Execute pour implémenter les fonctionnalités de l’activité. La figure 33 montre la classe d’activité terminée.
classe publique SendMail : CodeActivity<SmtpStatusCode>
{
public InArgument<string> To { get; set; }
public InArgument<string> From { get; set; }
public InArgument<string> Subject { get; set; }
public InArgument<string> Body { get; set; }
protected override SmtpStatusCode Execute(
Contexte CodeActivityContext)
{
Essayer
{
SmtpClient client = new SmtpClient();
Client. Send(
From.Get(context), ToAddress.Get(context),
Subject.Get(context), MailBody.Get(context));
}
catch (SmtpException smtpEx)
{
retourne smtpEx.StatusCode ;
}
retourne SmtpStatusCode.Ok ;
}
}
Figure 33 : Activité personnalisée SendMail
Il y a plusieurs points à noter sur l’utilisation des arguments. J’ai déclaré plusieurs propriétés .NET standard à l’aide des types InArgument<T>. C’est ainsi qu’une activité basée sur du code définit ses arguments d’entrée et de sortie. Toutefois, dans le code, je ne peux pas simplement référencer ces propriétés pour obtenir la valeur de l’argument. Je dois utiliser l’argument comme une sorte de handle pour récupérer la valeur à l’aide de l’ActivityContext fourni ; dans ce cas, un CodeActivityContext. Vous utilisez la méthode Get de la classe d’argument pour récupérer la valeur et la méthode Set pour affecter une valeur à un argument.
Une fois que l’activité a terminé la méthode Execute, le runtime le détecte et suppose que l’activité est terminée et la ferme. Pour un travail asynchrone ou de longue durée, il existe des moyens de modifier ce comportement qui sont expliqués dans une section à venir, mais dans ce cas, une fois l’e-mail envoyé, le travail est terminé.
Examinons maintenant une activité à l’aide de la classe de base NativeActivity pour gérer les activités enfants. Je vais créer une activité Itérator qui exécute une activité enfant un nombre donné de fois. Tout d’abord, j’ai besoin d’un argument pour contenir le nombre d’itérations à effectuer, d’une propriété pour l’exécution de l’activité et d’une variable pour contenir le nombre actuel d’itérations. La méthode Execute appelle une méthode BeginIteration pour démarrer l’itération initiale et les itérations suivantes sont appelées de la même façon. Notez dans la figure 34 que les variables sont manipulées de la même façon que les arguments à l’aide de la méthode Get et Set.
itérateur de classe publique : NativeActivity
{
public Activity Body { get; set; }
public InArgument<int> RequestedIterations { get; set; }
public Variable<int> CurrentIteration { get; set; }
public Itérator()
{
CurrentIteration = new Variable<int> { Default = 0 };
}
protected override void Execute(NativeActivityContext context)
{
BeginIteration(context) ;
}
private void BeginIteration(NativeActivityContext context)
{
if (RequestedIterations.Get(context) > CurrentIteration.Get(context))
{
Contexte. ScheduleActivity(Body,
new CompletionCallback(ChildComplete),
new FaultCallback(ChildFaulted));
}
}
}
Figure 34 : Activité native itérateur
Si vous examinez attentivement la méthode BeginIteration, vous remarquerez l’appel de méthode ScheduleActivity. C’est ainsi qu’une activité peut interagir avec le runtime pour planifier une autre activité pour l’exécution, et c’est parce que vous avez besoin de cette fonctionnalité que vous dérivez de NativeActivity. Notez également que lors de l’appel de la méthode ScheduleActivity sur ActivityExecutionContext, deux méthodes de rappel sont fournies. Étant donné que vous ne savez pas quelle activité sera utilisée comme corps, ni combien de temps elle prendra, et que WF est un environnement de programmation fortement asynchrone, les rappels sont utilisés pour notifier votre activité lorsqu’une activité enfant est terminée, ce qui vous permet d’écrire du code pour réagir.
Le deuxième rappel est un FaultCallback qui sera appelé si l’activité enfant lève une exception. Dans ce rappel, vous recevez l’exception et vous avez la possibilité, via l’ActivityFaultContext, d’indiquer au runtime que l’erreur a été gérée , ce qui l’empêche d’être en panne de l’activité. La figure 35 montre les méthodes de rappel pour l’activité Itérator où la planification de l’itération suivante et l’erreur FaultCallback gère l’erreur pour permettre à l’exécution de continuer à l’itération suivante.
private void ChildComplete(NativeActivityContext context,
ActivityInstance instance)
{
CurrentIteration.Set(context, CurrentIteration.Get(context) + 1);
BeginIteration(context) ;
}
private void ChildFaulted(NativeActivityFaultContext context, Exception ex,
ActivityInstance instance)
{
CurrentIteration.Set(context, CurrentIteration.Get(context) + 1);
Contexte. HandleFault();
}
Figure 35 : Rappels d’activité
Lorsque votre activité doit effectuer un travail qui peut être de longue durée, dans l’idéal, ce travail peut être transféré à un autre thread pour permettre au thread de flux de travail d’être utilisé pour continuer à traiter d’autres activités, ou utilisé pour traiter d’autres flux de travail. Toutefois, le simple démarrage des threads et le retour du contrôle au runtime peuvent causer des problèmes, car le runtime ne surveille pas les threads que vous créez. Si le runtime a déterminé que le flux de travail était inactif, le flux de travail peut se décharger, supprimer vos méthodes de rappel, ou le runtime peut supposer que votre activité est terminée et passer à l’activité suivante dans le workflow. Pour prendre en charge la programmation asynchrone dans votre activité, vous dérivez de AsyncCodeActivity ou AsyncCodeActivity<T>.
La figure 36 montre un exemple d’activité asynchrone qui charge un flux RSS. Cette signature de la méthode execute est différente pour les activités asynchrones en ce qu’elle retourne un IAsyncResult. Vous écrivez le code dans la méthode execute pour commencer votre opération asynchrone. Remplacez la méthode EndExecute pour gérer le rappel lorsque votre opération asynchrone est terminée et retournez le résultat de l’exécution.
classe scellée publique GetFeed : AsyncCodeActivity<SyndicationFeed>
{
public InArgument<URI> FeedUrl { get; set; }
protected override IAsyncResult BeginExecute(
Contexte AsyncCodeActivityContext, rappel AsyncCallback,
état de l’objet)
{
var req = (HttpWebRequest)HttpWebRequest.Create(
FeedUrl.Get(context));
Req. Méthode = « GET »;
Contexte. UserState = req ;
renvoyer req. BeginGetResponse(new AsyncCallback(callback), state);
}
protected override SyndicationFeed EndExecute(
Contexte AsyncCodeActivityContext, résultat IAsyncResult)
{
HttpWebRequest req = contexte. UserState en tant que HttpWebRequest ;
WebResponse wr = req. EndGetResponse(result) ;
SyndicationFeed localFeed = SyndicationFeed.Load(
XmlReader.Create(wr. GetResponseStream()));
retourner localFeed ;
}
}
Figure 36 : Création d’activités asynchrones
Concepts d’activité supplémentaires
Maintenant que vous avez vu les principes de base de la création d’activités à l’aide des classes de base, des arguments et des variables, et de la gestion de l’exécution ; Je vais aborder brièvement certaines fonctionnalités plus avancées que vous pouvez utiliser dans le développement d’activités.
Les signets permettent à un auteur d’activité de créer un point de reprise dans l’exécution d’un workflow. Une fois qu’un signet a été créé, il peut être repris pour continuer le traitement du flux de travail à partir de l’endroit où il s’est arrêté. Les signets étant enregistrés dans le cadre de l’état, contrairement aux activités asynchrones, une fois qu’un signet a été créé, il n’est pas un problème pour le flux de travail instance d’être conservé et déchargé. Lorsque l’hôte souhaite reprendre le flux de travail, il charge le flux de travail instance et appelle la méthode ResumeBookmark pour reprendre le instance là où il s’est arrêté. Lors de la reprise des signets, les données peuvent également être transmises à l’activité. La figure 37 montre une activité ReadLine qui crée un signet pour recevoir une entrée et inscrit une méthode de rappel à appeler lorsque les données arrivent. Le runtime sait quand une activité a des signets en attente et ne ferme pas l’activité tant que le signet n’a pas été repris. La méthode ResumeBookmark peut être utilisée sur la classe WorkflowApplication pour envoyer des données au signet nommé et signaler le signetCallback.
classe publique ReadLine : chaîne NativeActivity<>
{
public OutArgument<string> InputText { get; set; }
protected override void Execute(NativeActivityContext context)
{
Contexte. CreateBookmark(« ReadLine »,
new BookmarkCallback(BookmarkResumed));
}
private void BookmarkResumed(NativeActivityContext context,
Signet bk, état de l’objet)
{
Result.Set(context, state) ;
}
}
Figure 37 : Création d’un signet
Une autre fonctionnalité puissante pour les auteurs d’activité est le concept d’une ActivityAction. ActivityAction est l’équivalent du flux de travail de la classe Action dans le code impératif : décrivant un délégué commun ; mais pour certains, l’idée d’un modèle peut être plus facile à comprendre. Considérez l’activité T> ForEach<qui vous permet d’itérer sur un ensemble de données et de planifier une activité enfant pour chaque élément de données. Vous avez besoin d’un moyen pour permettre au consommateur de votre activité de définir le corps et de pouvoir consommer l’élément de données de type T. Essentiellement, vous avez besoin d’une activité capable d’accepter un élément de type T et d’agir sur celui-ci. ActivityAction<T> est utilisé pour activer ce scénario et fournit une référence à l’argument et à la définition d’une activité pour traiter l’élément. Vous pouvez utiliser ActivityAction dans vos activités personnalisées pour permettre aux consommateurs de votre activité d’ajouter leurs propres étapes aux points appropriés. Cela vous permet de créer des modèles de flux de travail ou d’activité d’une sorte, où un consommateur peut remplir les parties pertinentes pour personnaliser l’exécution pour leur utilisation. Vous pouvez également utiliser le TResult> ActivityFunc<et les alternatives associées lorsque le délégué que votre activité doit appeler retourne une valeur.
Enfin, les activités peuvent être validées pour s’assurer qu’elles sont correctement configurées en vérifiant les paramètres individuels, en limitant les activités enfants autorisées, etc. Pour la nécessité courante d’exiger un argument particulier, vous pouvez ajouter un attribut RequiredArgument à la déclaration de propriété dans l’activité. Pour une validation plus impliquée, dans le constructeur de votre activité, créez une contrainte et ajoutez-la à la collection de contraintes exposées sur la classe Activity. Une contrainte est une activité écrite pour inspecter l’activité cible et en garantir la validité. La figure 38 montre le constructeur de l’activité Itérator, qui valide que la propriété RequestedIterations est définie. Enfin, en remplaçant la méthode CacheMetadata, vous pouvez appeler la méthode AddValidationError sur le paramètre ActivityMetdata pour ajouter des erreurs de validation pour votre activité.
var act = new DelegateInArgument<Iterator> { Name = « constraintArg » };
var vctx = new DelegateInArgument<ValidationContext>();
Contrainte<Iterator> cons = new Constraint<Iterator Iterator>
{
Body = new ActivityAction<Iterator, ValidationContext>
{
Argument1 = acte,
Argument2 = vctx,
Gestionnaire = new AssertValidation
{
Message = « L’itération doit être fournie »,
PropertyName = « RequestedIterations »,
Assertion = new InArgument<bool>(
(e) => acte. Get(e). RequestedIterations != null)
}
}
};
Base. Constraints.Add(cons) ;
Figure 38 : Contraintes
Concepteurs d’activités
L’un des avantages de WF est qu’il vous permet de programmer votre logique d’application de manière déclarative, mais la plupart des gens ne veulent pas écrire DU XAML à la main, c’est pourquoi l’expérience du concepteur dans WF est si importante. Lors de la création d’activités personnalisées, vous voudrez probablement également créer un concepteur pour fournir l’expérience d’affichage et d’interaction visuelle aux consommateurs de votre activité. Le concepteur pour WF est basé sur Windows Presentation Foundation ce qui signifie que vous disposez de toute la puissance du style, des déclencheurs, de la liaison de données et de tous les autres outils de création d’une interface utilisateur riche pour votre concepteur. En outre, WF fournit certains contrôles utilisateur que vous pouvez utiliser dans votre concepteur pour simplifier la tâche d’affichage d’une activité enfant individuelle ou d’une collection d’activités. Les quatre contrôles principaux sont les suivants :
- ActivityDesigner : contrôle WPF racine utilisé dans les concepteurs d’activités
- WorkflowItemPresenter : utilisé pour afficher une seule activité
- WorkflowItemsPresenter : utilisé pour afficher une collection d’activités enfants
- ExpressionTextBox : permet d’activer la modification à la place d’expressions telles que des arguments
WF4 contient un modèle de projet ActivityDesignerLibrary et un modèle d’élément ActivityDesigner qui peuvent être utilisés pour créer initialement les artefacts. Une fois le concepteur initialisé, vous pouvez commencer à utiliser les éléments ci-dessus pour personnaliser l’apparence de votre activité en expliquant comment afficher les données et les représentations visuelles des activités enfants. Dans le concepteur, vous avez accès à un Objet ModelItem qui est une abstraction sur l’activité réelle et qui met en surface toutes les propriétés de l’activité.
Le contrôle WorkflowItemPresenter peut être utilisé pour afficher une seule activité qui fait partie de votre activité. Par exemple, pour permettre de faire glisser une activité vers l’activité Itérator illustrée précédemment et d’afficher l’activité contenue dans l’activité Iteractor, vous pouvez utiliser le code XAML de la figure 39, en liant workflowItemPresenter à ModelItem.Body. La figure 40 montre le concepteur en cours d’utilisation sur une aire de conception de flux de travail .
<sap:ActivityDesigner x:Class="CustomActivities.Presentation.IteratorDesigner »
xmlns= »https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x= »https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;
assembly=System.Activities.Presentation »
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;
assembly=System.Activities.Presentation »>
<Grille>
<Border BorderBrush="MidnightBlue » BorderThickness="3 » CornerRadius="10 »>
<sap:WorkflowItemPresenter MinHeight="50 »
Item="{Binding Path=ModelItem.Body, Mode=TwoWay} »
HintText="Add body here » />
</Frontière>
</Grille>
</sap:ActivityDesigner>
Figure 39 : WorkflowItemPresenter dans le concepteur d’activités
Figure 40 : Concepteur Iterator
WorkflowItemsPresenter fournit des fonctionnalités similaires pour afficher plusieurs éléments tels que les enfants dans une séquence. Vous pouvez définir un modèle et une orientation pour la façon dont vous souhaitez afficher les éléments, ainsi qu’un modèle d’espacement pour ajouter des éléments visuels entre des activités telles que des connecteurs, des flèches ou quelque chose de plus intéressant. La figure 41 montre un concepteur simple pour une activité de séquence personnalisée qui affiche les activités enfants avec une ligne horizontale entre chacune d’elles.
<sap:ActivityDesigner x:Class="CustomActivities.Presentation.CustomSequenceDesigner »
xmlns= »https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x= »https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;
assembly=System.Activities.Presentation »
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;
assembly=System.Activities.Presentation »>
<Grille>
<StackPanel>
<Border BorderBrush="Goldenrod » BorderThickness="3 »>
<sap:WorkflowItemsPresenter HintText="Drop Activities Here »
Items="{Binding Path=ModelItem.ChildActivities} »>
<sap:WorkflowItemsPresenter.SpacerTemplate>
<DataTemplate>
<Rectangle Height="3 » Width="40 »
Fill="MidnightBlue » Margin="5 » />
</Datatemplate>
</sap:WorkflowItemsPresenter.SpacerTemplate>
<sap:WorkflowItemsPresenter.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</sap:WorkflowItemsPresenter.ItemsPanel>
</sap:WorkflowItemsPresenter>
</Frontière>
</Stackpanel>
</Grille>
</sap:ActivityDesigner>
Figure 41 : Concepteur de séquences personnalisé à l’aide de WorkflowItemsPresenter
Figure 42 : Concepteur de séquences
Enfin, le contrôle ExpressionTextBox permet la modification sur place des arguments d’activité dans le concepteur en plus de la grille de propriétés. Dans Visual Studio 2010, cela inclut la prise en charge d’IntelliSense pour les expressions entrées. La figure 43 montre un concepteur pour l’activité GetManager créée précédemment, permettant la modification des arguments EmployeeName et ManagerEmail dans le concepteur. Le concepteur réel est illustré dans la figure 44.
<sap:ActivityDesigner x:Class="CustomActivities.Presentation.GetManagerDesigner »
xmlns= »https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x= »https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;
assembly=System.Activities.Presentation »
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;
assembly=System.Activities.Presentation »
xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;
assembly=System.Activities.Presentation »>
<sap:ActivityDesigner.Resources>
<sapc:ArgumentToExpressionConverter
x:Key="ArgumentToExpressionConverter »
x:Uid="swdv:ArgumentToExpressionConverter_1 » />
</sap:ActivityDesigner.Resources>
<Grille>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50 » />
<ColumnDefinition Width="* » />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock VerticalAlignment="Center »
HorizontalAlignment="Center">Employee:</TextBlock>
<sapv:ExpressionTextBox Grid.Column="1 »
Expression="{Binding Path=ModelItem.EmployeeName, Mode=TwoWay,
Converter={StaticResource ArgumentToExpressionConverter},
ConverterParameter=In} » OwnerActivity="{Binding Path=ModelItem} »
MinLines="1 » MaxLines="1 » MinWidth="50 »
HintText="< Nom de&l’employé gt; » />
<TextBlock Grid.Row="1 » VerticalAlignment="Center »
HorizontalAlignment="Center">Mgr Email:</TextBlock>
<sapv:ExpressionTextBox Grid.Column="2 » Grid.Row="1 »
UseLocationExpression="True »
Expression="{Binding Path=ModelItem.ManagerEmail, Mode=TwoWay,
Converter={StaticResource ArgumentToExpressionConverter},
ConverterParameter=Out} » OwnerActivity="{Binding Path=ModelItem} »
MinLines="1 » MaxLines="1 » MinWidth="50 » />
</Grille>
</sap:ActivityDesigner>
Figure 43 : Utilisation d’ExpressionTextBox pour modifier des arguments dans le concepteur
Figure 44 : Concepteur d’activités GetManager
Les exemples de concepteurs présentés ici étaient simples pour mettre en évidence les fonctionnalités spécifiques, mais toute la puissance de WPF est à votre disposition pour rendre vos activités plus faciles à configurer et plus naturelles à utiliser pour vos consommateurs. Une fois que vous avez créé un concepteur, il existe deux façons de l’associer à l’activité : appliquer un attribut à la classe d’activité ou implémenter l’interface IRegisterMetadata. Pour permettre à la définition d’activité de choisir le concepteur, appliquez simplement l’attribut DesignerAttribute à la classe d’activité et fournissez le type pour le concepteur ; cela fonctionne exactement comme dans WF3. Pour une implémentation plus faiblement couplée, vous pouvez implémenter l’interface IRegisterMetadata et définir les attributs que vous souhaitez appliquer à la classe d’activité et aux propriétés. La figure 45 montre un exemple d’implémentation pour appliquer les concepteurs pour deux activités personnalisées. Visual Studio découvre votre implémentation et l’appelle automatiquement, tandis qu’un hôte de concepteur personnalisé, décrit ci-dessous, appelle la méthode explicitement.
public class Metadata : IRegisterMetadata
{
public void Register()
{
AttributeTableBuilder builder = new AttributeTableBuilder();
Constructeur. AddCustomAttributes(typeof(SendMail), new Attribute[] {
new DesignerAttribute(typeof(SendMailDesigner)))});
Constructeur. AddCustomAttributes(typeof(GetManager), new Attribute[]{
new DesignerAttribute(typeof(GetManagerDesigner))});
MetadataStore.AddAttributeTable(builder. CreateTable());
}
}
Figure 45 : Inscription de concepteurs pour les activités
Réhébergement du concepteur de flux de travail
Dans le passé, les développeurs voulaient souvent offrir à leurs utilisateurs la possibilité de créer ou de modifier des flux de travail. Dans les versions précédentes de Windows Workflow Foundation, cela était possible, mais pas une tâche triviale. Avec le passage à WPF, un nouveau concepteur et le réhébergement a été un cas d’usage privilégié pour l’équipe de développement. Vous pouvez héberger le concepteur dans une application WPF personnalisée, comme le montre la figure 46 avec quelques lignes de XAML et quelques lignes de code.
Figure 46 : Concepteur de flux de travail réhébergé
Le code XAML de la fenêtre, figure 47, utilise une grille pour mettre en page trois colonnes, puis place un contrôle de bordure dans chaque cellule. Dans la première cellule, la boîte à outils est créée de manière déclarative, mais peut également être créée dans le code. Pour le mode concepteur lui-même et la grille des propriétés, il existe deux contrôles de bordure vides dans chacune des cellules restantes. Ces contrôles sont ajoutés dans le code.
<Window x:Class="WorkflowEditor.MainWindow »
xmlns= »https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x= »https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib »
xmlns:sapt="clr-namespace:System.Activities.Presentation.Toolbox;
assembly=System.Activities.Presentation »
Title="Workflow Editor » Height="500 » Width="700 » >
<Window.Resources>
<sys:String x:Key="AssemblyName">System.Activities, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35</sys:String>
<sys:String x:Key="MyAssemblyName">CustomActivities</sys:String>
</Window.Resources>
<Grid x:Name="DesignerGrid »>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2* » />
<ColumnDefinition Width="7* » />
<ColumnDefinition Width="3* » />
</Grid.ColumnDefinitions>
<Border>
<sapt:ToolboxControl>
<sapt:ToolboxControl.Categories>
<sapt:ToolboxCategory CategoryName="Basic »>
<sapt:ToolboxItemWrapper
AssemblyName="{StaticResource AssemblyName} » >
<sapt:ToolboxItemWrapper.ToolName>
System.Activities.Statements.Sequence
</sapt:ToolboxItemWrapper.ToolName>
</sapt:ToolboxItemWrapper>
<sapt:ToolboxItemWrapper
AssemblyName="{StaticResource MyAssemblyName} »>
<sapt:ToolboxItemWrapper.ToolName>
CustomActivities.SendMail
</sapt:ToolboxItemWrapper.ToolName>
</sapt:ToolboxItemWrapper>
</sapt:ToolboxCategory>
</sapt:ToolboxControl.Categories>
</sapt:ToolboxControl>
</Frontière>
<Border Name="DesignerBorder » Grid.Column="1 »
BorderBrush="Salmon » BorderThickness="3 » />
<Border x:Name="PropertyGridExpander » Grid.Column="2 » />
</Grille>
</Fenêtre>
Figure 47 : Designer réhébergement XAML
Le code permettant d’initialiser le concepteur et la grille de propriétés est illustré dans la figure 48. La première étape, dans la méthode OnInitialized, consiste à inscrire les concepteurs pour toutes les activités qui seront utilisées dans le concepteur de flux de travail. La classe DesignerMetadata inscrit les concepteurs associés pour les activités fournies avec l’infrastructure et la classe Metadata inscrit les concepteurs pour mes activités personnalisées. Ensuite, créez la classe WorkflowDesigner et utilisez les propriétés View et PropertyInspectorView pour accéder aux objets UIElement nécessaires au rendu. Le concepteur est initialisé avec une séquence vide, mais vous pouvez également ajouter des menus et charger le code XAML de workflow à partir de diverses sources, notamment le système de fichiers ou une base de données.
public MainWindow()
{
InitializeComponent();
}
protected override void OnInitialized(EventArgs e) {
Base. OnInitialized(e);
RegisterDesigners();
PlaceDesignerAndPropGrid();
}
private void RegisterDesigners() {
new DesignerMetadata(). Register();
new CustomActivities.Presentation.Metadata(). Register();
}
private void PlaceDesignerAndPropGrid() {
Concepteur WorkflowDesigner = new WorkflowDesigner();
Designer. Load(new System.Activities.Statements.Sequence());
DesignerBorder.Child = designer. Vue;
PropertyGridExpander.Child = designer. PropertyInspectorView;
}
Figure 48 : Designer réhébergement du code
Avec ce petit peu de code et de balisage, vous pouvez modifier un workflow, glisser-déplacer des activités à partir de la boîte à outils, configurer des variables dans le flux de travail et manipuler des arguments sur les activités. Vous pouvez également obtenir le texte du code XAML en appelant Flush sur le concepteur, puis en référençant la propriété Text du WorkflowDesigner. Il y a beaucoup plus que vous pouvez faire, et commencer est beaucoup plus facile qu’avant.
Services de workflow
Comme mentionné précédemment, l’un des grands domaines d’intérêt de cette version de Windows Workflow Foundation est une intégration plus étroite avec Windows Communication Foundation. L’exemple de la procédure pas à pas de l’activité a montré comment appeler un service à partir d’un flux de travail. Dans cette section, je vais expliquer comment exposer un flux de travail en tant que service WCF.
Pour commencer, créez un projet et sélectionnez le projet Service de flux de travail WCF. Une fois le modèle terminé, vous trouverez un projet avec un fichier web.config et un fichier avec une extension XAMLX. Le fichier XAMLX est un service déclaratif ou un service de flux de travail et, comme d’autres modèles WCF, fournit une implémentation de service fonctionnelle, bien que simple, qui reçoit une demande et retourne une réponse. Pour cet exemple, je vais modifier le contrat pour créer une opération pour recevoir une note de frais et retourner un indicateur indiquant que le rapport a été reçu. Pour commencer, ajoutez une classe ExpenseReport au projet, comme celle illustrée dans la figure 49.
[DataContract]
classe publique ExpenseReport
{
[DataMember]
public DateTime FirstDateOfTravel { get; set; }
[DataMember]
public double TotalAmount { get; set; }
[DataMember]
chaîne publique EmployeeName { get; set; }
}
Figure 49 : Type de contrat de note de frais
De retour dans le concepteur de flux de travail, remplacez le type de la variable « data » dans les variables par le type ExpenseReport que vous venez de définir (vous devrez peut-être générer le projet pour que le type s’affiche dans la boîte de dialogue du sélecteur de types). Mettez à jour l’activité Recevoir en cliquant sur le bouton Contenu et en remplaçant le type dans la boîte de dialogue par le type ExpenseReport à mettre en correspondance. Mettez à jour les propriétés de l’activité pour définir un nouveau OperationName et ServiceContractName, comme illustré dans la figure 50.
Figure 50 : Configuration de l’emplacement de réception
À présent, l’activité SendReply peut avoir la valeur Valeur définie sur True pour retourner l’indicateur de réception. Évidemment, dans un exemple plus complexe, vous pouvez utiliser toute la puissance du flux de travail pour enregistrer des informations dans la base de données, valider le rapport, etc. avant de déterminer la réponse. L’accès au service avec l’application WCFTestClient montre comment la configuration d’activité définit le contrat de service exposé, comme illustré dans la figure 51.
Figure 51 : Contrat généré à partir du service de workflow
Une fois que vous avez créé un service de workflow, vous devez l’héberger, comme vous le feriez avec n’importe quel service WCF. L’exemple précédent utilisait l’option la plus simple pour l’hébergement : IIS. Pour héberger un service de flux de travail dans votre propre processus, vous devez utiliser la classe WorkflowServiceHost qui se trouve dans l’assembly System.ServiceModel.Activities. Notez qu’il existe une autre classe portant ce même nom dans l’assembly System.WorkflowServices qui est utilisée pour héberger des services de flux de travail créés à l’aide des activités WF3. Dans le cas le plus simple de l’auto-hébergement, vous pouvez utiliser du code comme celui indiqué dans la figure 52 pour héberger votre service et ajouter des points de terminaison.
Hôte WorkflowServiceHost = nouveau
WorkflowServiceHost(XamlServices.Load(« ExpenseReportService.xamlx »),
new URI(« https://localhost:9897/Services/Expense"));
Hôte. AddDefaultEndpoints();
Hôte. Description.Behaviors.Add(
new ServiceMetadataBehavior { HttpGetEnabled = true });
Hôte. Open();
Console.WriteLine(« Host is open »);
Console.ReadLine();
Figure 52 : Auto-hébergement du service de flux de travail
Si vous souhaitez tirer parti des options d’hébergement managé dans Windows, vous pouvez héberger votre service dans IIS en copiant le fichier XAMLX dans le répertoire et en spécifiant toutes les informations de configuration nécessaires pour les comportements, les liaisons, les points de terminaison, etc. dans le fichier web.config. En fait, comme indiqué précédemment, lorsque vous créez un projet dans Visual Studio à l’aide de l’un des modèles de projet Service de flux de travail déclaratif, vous obtenez un projet avec un fichier web.config et le service de flux de travail défini dans un XAMLX prêt à être déployé.
Lorsque vous utilisez la classe WorkflowServiceHost pour héberger votre service de flux de travail, vous devez toujours être en mesure de configurer et de contrôler les instances de flux de travail. Pour contrôler le service, il existe un nouveau point de terminaison standard appelé WorkflowControlEndpoint. Une classe complémentaire, WorkflowControlClient, fournit un proxy client prédéfini. Vous pouvez exposer un WorkFlowControlEndpoint sur votre service et utiliser WorkflowControlClient pour créer et exécuter de nouvelles instances de flux de travail, ou contrôler les flux de travail en cours d’exécution. Vous utilisez ce même modèle pour gérer les services sur l’ordinateur local, ou vous pouvez l’utiliser pour gérer les services sur un ordinateur distant. La figure 53 montre un exemple mis à jour d’exposition du point de terminaison de contrôle de workflow sur votre service.
Hôte WorkflowServiceHost = nouveau
WorkflowServiceHost(« ExpenseReportService.xamlx »,
new URI(« https://localhost:9897/Services/Expense"));
Hôte. AddDefaultEndpoints();
WorkflowControlEndpoint wce = new WorkflowControlEndpoint(
new NetNamedPipeBinding(),
new EndpointAddress(« net.pipe://localhost/Expense/WCE »));
Hôte. AddServiceEndpoint(wce);
Hôte. Open();
Figure 53 : Point de terminaison du contrôle de flux de travail
Étant donné que vous ne créez pas directement workflowInstance, il existe deux options pour ajouter des extensions au runtime. La première est que vous pouvez ajouter des extensions à WorkflowServiceHost et qu’elles s’initialisent correctement avec vos instances de workflow. La deuxième consiste à utiliser la configuration. Le système de suivi, par exemple, peut être entièrement défini dans le fichier de configuration de l’application. Vous définissez les participants de suivi et les profils de suivi dans le fichier de configuration, et à l’aide d’un comportement, vous appliquez un participant particulier à un service, comme illustré dans la figure 54.
<system.serviceModel>
<tracking>
<participants>
<add name="EtwTrackingParticipant »
type="System.Activities.Tracking.EtwTrackingParticipant, System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 »
profileName="HealthMonitoring_Tracking_Profile"/>
</Participants>
<Profils>
<trackingProfile name="HealthMonitoring_Tracking_Profile »>
<workflow activityDefinitionId="* »>
<workflowInstanceQuery>
<states>
<state name="Started"/>
<state name="Completed"/>
</États>
</workflowInstanceQuery>
</Workflow>
</Trackingprofile>
</Profils>
</Suivi>
. . .
<behaviors>
<serviceBehaviors>
<behavior name="SampleTrackingSample.SampleWFBehavior »>
<etwTracking profileName= » HealthMonitoring_Tracking_Profile » />
</Comportement>
</serviceBehaviors>
</Comportements>
. . .
Figure 54 : Configuration du suivi pour les services
Il existe de nombreuses nouvelles améliorations à l’hébergement et à la configuration des services WCF dont vous pouvez tirer parti dans vos flux de travail, tels que les services « .svc-less », ou les services qui n’ont pas besoin d’une extension de fichier, de liaisons par défaut et de points de terminaison par défaut, pour n’en citer que quelques-uns. Pour plus d’informations sur ces fonctionnalités et d’autres dans WCF4, reportez-vous au document complémentaire sur WCF qui se trouve dans la section Ressources supplémentaires.
Outre les améliorations apportées à l’hébergement, Windows Workflow Foundation tire parti d’autres fonctionnalités de WCF ; certains directement et d’autres indirectement. Par exemple, la corrélation de message est désormais une fonctionnalité clé dans la création de services qui vous permet de lier des messages à un flux de travail donné instance en fonction des données du message, telles qu’un numéro de commande ou un ID d’employé. La corrélation peut être configurée sur les différentes activités de messagerie pour la demande et la réponse et prend en charge la corrélation sur plusieurs valeurs dans le message. Là encore, vous trouverez plus d’informations sur ces nouvelles fonctionnalités dans le document complémentaire sur WCF4.
Conclusion
La nouvelle technologie Windows Workflow Foundation fournie avec .NET Framework 4 représente une amélioration majeure des performances et de la productivité des développeurs et réalise un objectif de développement d’applications déclaratives pour la logique métier. L’infrastructure fournit les outils permettant de simplifier de courtes unités de travail complexes et de créer des services complexes à long terme avec des messages corrélés, un état persistant et une visibilité enrichie des applications via le suivi.
À propos de l’auteur
Matt est membre du personnel technique de Pluralsight, où il se concentre sur les technologies de systèmes connectés (WCF, WF, BizTalk, AppFabric et la plateforme de services Azure). Matt est également consultant indépendant spécialisé dans la conception et le développement d’applications Microsoft .NET. En tant que rédacteur, Matt a contribué à plusieurs revues et magazines, y compris MSDN Magazine où il écrit actuellement le contenu du flux de travail pour la colonne Foundations. Matt partage régulièrement son amour de la technologie en prenant la parole lors de conférences locales, régionales et internationales telles que Tech Ed. Microsoft a reconnu Matt comme MVP pour sa communauté contributions autour de la technologie des systèmes connectés. Contactez Matt via son blog : http://www.pluralsight.com/community/blogs/matt/default.aspx.