Dans Windows Worklfow Foundation (WF), on tire beaucoup profit des avancées de Windows Presentation Foundation (WPF). On en tire tellement profit, que quand un souci de design se présente, il faut presque toujours lorgner du côté de WPF pour trouver une solution.

Etrangement une question récurrente en ce moment concerne le code à ajouter pour associer un ActivityDesigner à une Activity. Ce problème a une solution simple et une seconde un peu moins évidente (la seconde pouvant avoir deux ou trois variantes):

1 - L’attribut Designer ajouté à l’activité (l’approche simple)

[Designer(typeof(YourActivityDesigner))]
public class YourActivity : Activity
{
  // ...
}

Ce code est simple, il n’y a pas grand chose à dire à son sujet.

Inconvénient :

  • L’activité connait sont Desinger, donc le designer est référencé avec l’activité et ne peut pas être dissocier.
  • Si on fait un assembly Acivities et un second Design, le second devra être mis en référence du premier. On devra donc toujours distribuer les deux assemblies, même si l’utilisateur final n’utilise pas de designer (on risque de charger des références inutiles).

D’où le graphe de référence suivant  :

wf4_activity_designer0

2 - L’enregistrement des metadata par code dans l’ ActivityDesigner (l’approche la moins drôle, et pas forcement le plus facile à gérer)

Pas forcément ma préféré, mais elle fonctionne bien et les activités ne sont pas liées à leurs designers. Les projets utilisant nos workflows non plus.

La metadata est construite par l’appel de la méthode Register de l’ActivityDesigner. Ceci se fait automatiquement quand on utilise Visual Studio.

public partial class MyActivityDesigner : IRegisterMetadata
{
    public void Register()
    {
        AttributeTableBuilder builder = new AttributeTableBuilder();

        builder.AddCustomAttributes(typeof(MyActivity),
            new DesignerAttribute(typeof(MyAcitivityDesigner)));

        MetadataStore.AddAttributeTable(builder.CreateTable());
    }

    // + code de l'ActivityDesigner...
}

Si la solution est découpée en deux projets : un contenant les activités et un second le design, le second doit avoir sa sortie qui pointe vers la sortie du premier. De plus il faut respecter un convention de nommage des projets.

Exemple :

  • MyCustomActivities.dll  : projet contenant les activités.
  • MyCustomActivities.Design.dll  : projet contenant les designers des activités.

Inconvénient :

  • Si les designers d’activités doivent être utilisé dans un projet qui re-host le designer de wotkflow, il faudra appeler les méthodes Register de chaque ActivityDesigner une à une (vraiment pas drôle).

D’où le graphe de référence suivant  :  

 wf4_activity_designer1

3 - L’enregistrement des metadata par code à partir d’une classe dédiée (l’approche la moins drôle, mais la plus intéressante pour avoir une belle architecture et permettre une évolution simple)

On garde la même idée que pour le précédent cas et les mêmes conventions de nommage si on sépare la solution en plusieurs projets. Petite nouveauté, on code l’enregistrement de toutes les metadata dans une seule et unique classe :

Voici un petit exemples avec 3 activités et 2 designers. Ceci permet de mettre en évidence que deux activités avec une interface commune, peuvent partager un même designer d’activité :

public class DesignerMetadata : IRegisterMetadata
{
    public void Register()
    {
        AttributeTableBuilder builder = new AttributeTableBuilder();

        builder.AddCustomAttributes(typeof(EntityScope),
            new DesignerAttribute(typeof(EntityScopeDesigner)));
        builder.AddCustomAttributes(typeof(AddEntity<>),
            new DesignerAttribute(typeof(AddEntityDesigner)));
        builder.AddCustomAttributes(typeof(DeleteEntity<>),
            new DesignerAttribute(typeof(AddEntityDesigner)));

        MetadataStore.AddAttributeTable(builder.CreateTable());
    }
}

Rien de bien compliqué

Inconvénient :

  • Comme pour le cas précédent, il faut procéder à l’appel de la méthode Register manuellement si on re-host le designer de workflows.

A mon avis, il s’agit là de la pratique la plus intéressante. Gros avantage : l’assembly Design peut être substitué par un autre par simple copier-colle de dll ;)

Le graphe de référence est similaire au précédent.

Vu que je ne pouvais pas m'arrêter en si bon chemin, on peut aussi envisager le découplage complet entre designer et activité en codant la classe DesignerMetadata hors de l’assembly contenant les designers. D’où une relation telle que celle-ci.

D’où le graphe de référence suivant  :

wf4_activity_designer2

Là on touche vraiment quelque chose de sympathique car les designers et leurs manières de s'intégrer ne dépendent plus du tout des activités et la classe DesignerMetadata peut jongler entre plusieurs assemblies de disingers.

Le projet workflows ne connait pas les designer, mais il les utilise…

J’adore ;)