Comprendre le fonctionnement des Rules de WF

Dieudonne N'Tamack

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Comprendre le fonctionnement des Rules de WF

- Auteur : Dieudonne N'Tamack

- Parution : 19 novembre 2008

- Mise à jour : 19 novembre 2008

- Licence : Copyright ® 2008 Dieudonné N'Tamack. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Comment utiliser une activité Policy ?

- III. Le chainage avant ? Mais qu'est-ce que c'est ?

- IV. Conclusion

I - Introduction

L'activité Policy va nous permettre de piloter nos workflows à partir de règles simples, mais efficaces que nous aurons au préalable définies.

Ce mécanisme va nous permettre de mettre en place des workflows rapidement, mais de manière très simple, à condition d'avoir compris comment ils fonctionnent, et là est l'objectif de cet article.

II - Comment utiliser une activité Policy ?

En fait, une activité Policy va contenir un ou plusieurs jeux de règles, appelés RuleSets, et chacun de ces RuleSets va contenir à son tour une ou plusieurs règles, appelées Rules, et une rule va être tout simplement une instruction If/Then/Else, ayant une priorité. La règle ayant la priorité la plus élevée est exécutée en premier.

Prenons un exemple très simple(*) :

Nous allons partir avec 7 valeurs : À = 0, B =0, C = 5, D = 2 et E = 0,

Et quatre règles :

- la 1re, de priorité = 1 : If (B == 5) { E = 7 ;} ;

- la 2e, de priorité = 2 : If (D==2) {A = 15 ;} ;

- la 3e, de priorité = 3 : If (C==5) {B = 10 ;} ;

- la 4e, de priorité = 4 : if(A==15) {B=5}.

Voici notre workflow modélisé.

Les activités Code vont uniquement nous permettre d'afficher la valeur de nos variables dans une console, et l'activité Policy va donc contenir une RuleSet avec nos règles que voici :

Si nous exécutons directement le code, voici la sortie de la console :

La 1re série de valeurs représente les valeurs avant l'application de l'activité Policy, tandis que la 2e série représente les valeurs après l'application de la Policy.

Si vous n'avez jamais travaillé avec l'activité Policy, vous pourriez vous attendre à l'exécution suivante :

- la règle 4, de priorité 4 est exécutée en 1er => If (A==15) -> false, donc pas d'action (toutes les branches Else sont vides).

- la règle 3, de priorité 3 est exécutée => If (C==5) -> true, donc on met à jour la valeur de B ; B=10.

- La règle 2, de priorité 2 est exécutée => if (D==2) -> true, donc on met à jour la valeur de A ; A=15.

- La règle 1, de priorité 1 est exécuté => if (B==5) -> false (depuis le 2) B a maintenant la valeur 10), donc pas d'action.

- Le workflow s'achève.

Cette séquence nous produit les valeurs suivantes : A=15, B=10, C=5, D=2 et E=0 ; or comme vous le constatez, ce n'est pas du tout le cas (mais ça aurait pu l'être !!). À présent, rentrons dans la magie des RuleSets.

En fait, lorsqu'un RuleSet s'exécute, voici comment les règles sont évaluées :

- La règle de plus haute priorité est d'abord évaluée ;

- Si l'évaluation de cette règle modifie une donnée (un champ ou une propriété) qui fait partie de la condition d'une autre règle, évaluer cette règle ;

- Si toutes les règles n'ont pas encore été évaluées, passer à la règle de plus haute priorité suivante.

Voici donc dans quel ordre les règles ont été précédemment évaluées, conduisant au résultat obtenu :

- la règle 4, de priorité 4 est exécutée en 1er => If (A==15) -> false, donc pas d'action (toutes les branches Else sont vides).

- la règle 3, de priorité 3 est exécutée => If (C==5) -> true, donc on met à jour la valeur de B ; B=10.

- La valeur de B a changé, donc la règle 1, indépendamment de sa priorité, est exécutée => if (B==5) -> false (depuis le 2) B a maintenant la valeur 10), donc pas d'action.

- La règle 2, de priorité 2 est exécutée => if (D==2) -> true, donc on met à jour la valeur de A ; A=15; à ce stade, toutes les règles ont été évaluées ; seulement, la valeur de A vient de changer, donc il faut à nouveau évaluer la règle 4 !

- La règle 4, de priorité 4 est exécutée en 1er => If (A==15) -> maintenant true, donc on met à jour la valeur de B ; B=5 ;

- La valeur de B a changé, donc la règle 1, indépendamment de sa priorité, est exécutée => if (B==5) -> true donc on met à jour la valeur de E ; E=7.

- La valeur de E a changé ; cependant E n'est utilisé dans aucune condition de règle ; de plus, toutes les règles du workflow ont été exécutées, donc le workflow s'achève.

Si on fait un bilan des valeurs, on a : A=15, B=5, C=5, D=2 et E=7 ; le résultat retourné par notre workflow !

(*** Si à ce stade, vous êtes déjà perdu, je vous suggère de refaire l'exemple sur un morceau de papier, en suivant pas à pas l'exécution des règles ***).

Ce que l'on vient de voir est en fait une des possibilités d'exécution d'un RuleSet. Cette particularité qu'a WF de « chainer » de cette façon les règles entre elles est plus connue sous le nom de « chainage avant », et il en existe de trois sortes : le chainage implicite, le chainage basé sur les attributs et le chainage explicite.

III - Le chainage avant ? Mais qu'est-ce que c'est ?

Le chainage implicite est celui que nous venons de voir ; des champs ou propriétés participant à la condition d'évaluation d'une règle sont directement (par simple affectation, pas par appel de méthodes) modifiés, entrainant la réévaluation de cette dernière.

Le chainage basé sur les attributs, va nous permettre d'utiliser des méthodes pour mettre à jour nos champs/propriétés, tout en conservant le comportement par défaut de WF ; par exemple, au lieu d'écrire dans ma Rule

//A écrire au niveau de la Rule

if (this.À == 15) this.B = 7;

Je pourrai écrire

//À écrire au niveau de la Rule
if (this.À == 15) UpdateB(7);

//Au niveau du fichier de code
[RuleWrite("B")]
private void UpdateB(int p){
this.B = p;
}

Ceci aurait pour action d'informer WF que la valeur de B est modifiée à chaque appel de B et donc de lancer la réévaluation de la Rule1 ; sans cet attribut, WF est incapable de détecter cette modification.

Ce comportement vous permet donc également de spécifier certaines règles à réévaluer, et d'autres à ignorer (en ne renseignant pas les attributs ; cependant, nous verrons plus bas comment demander à WF d'ignorer les réévaluations d'une manière plus élégante).

Côté attribut, il en existe trois, RuleRead, RuleWrite et RuleInvoke ; ces 3 attributs s'utilisent de la même manière.

L'attribut RuleRead va indiquer à WF qu'une propriété est lue, tandis que l'attribut RuleInvoke va être utilisé que la méthode appelle une autre méthode qui est susceptible de mettre à jour les valeurs et prend en paramètre le nom de la méthode ; par exemple :

//Au niveau du fichier de code
[RuleInvoke("UpdateB")]
private void UpdateValue() {
//...
UpdateB(7);
}
[RuleWrite("B")]
private void UpdateB(int p){
this.B = p;
}

Le dernier type de chainage va être le chainage explicite, qui va être en fait un moyen de « forcer » la réévaluation d'une règle ; on peut l'utiliser en remplacement de l'attribut RuleWrite ; ce type de chainage va surtout être utile lorsque l'on ne peut pas positionner d'attribut sur la méthode appelante ; imaginer que votre valeur soit mise à jour par exemple à partir d'une méthode contenue dans une classe définie au niveau de l'application hôte ; vous ne pourrez pas positionner vos attributs.

Maintenant que nous avons vu ce qu'est le chainage avant, voyons à présent ce que WF nous donne pour le contrôler.

Si nous retournons au niveau de notre Policy, puis que nous ouvrons notre éditeur de RuleSet, nous remarquons un champ chainage, ayant la valeur par défaut Chainage complet.

C'est avec cette option que nous travaillons depuis le début, et qui signale à WF d'évaluer les règles comme nous l'avons défini plus haut, en tenant compte des trois types de chainages.

L'option « Mise à jour uniquement » va dicter à WF de ne tenir compte que du chainage explicite ; ainsi, seuls les appels à la méthode Update() vont activer la réévaluation des règles ; les mises à jour implicites et/ou signalées par les attributs seront ignorées.

La dernière Option, Séquentiel, désactive complètement le chainage, et par la même occasion la réévaluation des règles.

Ce qui est intéressant de noter ici, c'est que si nous exécutons notre workflow initial en mode Séquentiel et en mode Mise à jour uniquement, nous obtiendrons non seulement le même résultat, qui serait le suivant (aucune règle n'est réévaluée, et le workflow est donc exécuté de manière séquentielle) :

Cependant, si nous changeons toutes les affectations de nos règles par des instructions UpdateValue (Attention, pas Update), comme ci-dessous

Le résultat sera le même, cette fois-ci en mode Chainage complet et en mode Mise à jour uniquement.

Tout à l'heure, je vous ai également parlé de la possibilité de ne réévaluer que certaines règles et pas d'autres, de manière plus élégante qu'en détournant le comportement des attributs, eh bien, il vous suffit de ne jamais choisir dans le mode de réévaluation.

De cette façon, seules les règles ayant l'attribut de réévaluation positionné à toujours seront réévaluées ; les autres ne seront évaluées qu'une fois.

Il existe cependant une petite subtilité qu'il faut connaitre ; si vous spécifiez le mode de réévaluation à Jamais, la règle n'est évaluée une fois que si une action (soit dans la branche Then, soit dans la branche Else) a été exécutée ; si par exemple dans notre cas, une règle est évaluée à false et donc passe dans la branche Else qui est vide (en d'autres termes aucune action n'est exécutée), WF réévaluera ultérieurement cette règle si il en a l'opportunité.

Par exemple, si on sélectionne la chaine complète, puis qu'on choisit le mode de réévaluation à Jamais, on obtient la sortie suivante :

Cela nous montre qu'une réévaluation a bien été exécutée, sinon on aurait eu comme valeur A=15, B=10, C=5, D=2 et E=0.

La solution consiste à rajouter une action qui ne fait rien comme ceci

private void DoNothing() {
//Ne fait absolument rien !
}

Et voici à présent le résultat !

IV - Conclusion

Au travers de cet article, j'espère avoir démystifié pour vous l'activité Policy et ses RuleSets, et j'espère que désormais vous pourrez utiliser cette activité de manière plus efficace, sans être surpris par les différentes sorties que peut générer cette activité.

(*)énoncé de l'exemple pris sur le site MSDN.