Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

On parle souvent des nouvelles fonctionnalités Azure, mais il y a parfois des améliorations qui passent inaperçues. Entre autres, l'amélioration du débit quand il faut télécharger des ISO de la MSDN à partir d'une VM.

Je crois qu'une capture d'écran est plus parlante qu'un long discours :

Rien à voir avec le débit très réduit que l'on pouvait constater au lancement de l'offre ;)

Dans mon précédent article, je décrivais une procédure plus ou moins fastidieuse pour simuler un Break. Cette manipulation peut très vite devenir rébarbative quand il faut la reproduire plisseurs fois dans un même projet. Heureusement, Workflow Foundation permet de coder des fabriques qui produisent des déclarations de workflows à partir d’un template.

Pour ceux qui ont du mal à comprendre cette dernière phrase :

Oui on part encore dans un grand délire à la mode WF (sans T au milieu, j’insiste), ou l’on va créer un code pour générer des activités en mode design et non en exécution. On n’est pas dans de la meta-programmation, mais comme souvent avec WF on a l’impression que l’on n’en est pas loin.

Pour coder une fabrique, il faut :

  • Créer une classe qui implémente IActivityTemplateFactory.
  • Coder la méthode Create qui va créer l’activité qu’il faut générer.

Si on veut reprendre mon exemple pour simuler un Break, on doit coder le Template suivant (pas forcément évident à écrire vu que l’on a une activité déléguée générique) :

/// <summary>
/// Template to include a break in ParallelForEach
/// </summary>
/// <typeparam name="T"></typeparam>
public class ParallelForEachBreak<T > : IActivityTemplateFactory
{
    public Activity Create(DependencyObject target)
    {
        return new TryCatch
        {
            Try = new ParallelForEach<T >
            {
                Body = new ActivityAction<T >
                {
                    Argument = new DelegateInArgument<T >("item"),
                    Handler = new Sequence
                    {
                        Activities = { new Break()}
                    }
                }
            },
            Catches =
            {
                new Catch<BreackException >()
            }
        };
    }
}

Celui-ci va retourner un TryCatch contenant un ParallelForEach et son Break (on ajoute une Sequence pour faciliter la vie de l’utilisateur final), ainsi qu’un catch capturant les exceptions de type BreakException.

Si on compile le projet, la fabrique est présente dans la toolbox, comme si il s’agissait d’une activité normale.

01_TemplateFactory

Si on la glisse dans un workflow, on se voit demander de choisir le type à utiliser pour notre activité générique :

02_TemplateFactory

Et pour finir les activités tant attendues:

03_TemplateFactory

Simple, efficace, mais tellement peu connu Clignement d'œil

Note: Voici un article qui m’a été demandé il y a bien longtemps et qui malheureusement s’est perdu dans ma boite à idées... désolé Triste

 

De base, il n’existe pas de notion de break dans Workflow Foundation (et par chance il n’y a pas non plus de GoTo). Pour disposer d’un break, il faut donc ruser. L’astuce consiste à utiliser la seule activité TryCatch et de lui faire capturer une exception particulière.

Pour l’occasion, j’ai une exception simple (autant dire qu’elle ne sert à rien seule):

public sealed class BreackException : Exception
{
}

Et une activité Break (ne pas oublier l’attribut DebuggerHidden pour ne pas bloquer le debugger)

/// <summary>
/// Throw a BreackException
/// </summary>
public sealed class Break : CodeActivity
{
    /// <summary>
    /// Execute
    /// </summary>
    /// <param name="context"></param>
    [DebuggerHidden()]
    protected override void Execute(CodeActivityContext context)
    {
        throw new BreackException();
    }

    /// <summary>
    /// Cache
    /// </summary>
    /// <param name="metadata"></param>
    protected override void CacheMetadata(CodeActivityMetadata metadata)
    {
        // Nothing to register
    }
}

Pour utiliser cet ensemble, il suffit d’ajouter une activité TryCatch. On utilise le lien “Add new catch”

01_BreakSimulation

 

On choisit ensuite de parcourir la liste des types d’exceptions disponibles

02_BreakSimulation

 

On choisit l’exception custom “BreakException” citée un peu plus haut. Dans mon exemple, elle se trouve dans le même projet (ne pas oublier de compiler le projet avant)

03_BreakSimulation

 

On revient ensuite au bloc Try en utilisant le lien “Add an activity” (en haut à droite du TryCatch)

05_BreakSimulation

On peut alors glisser son activité “Break”. Libre à vous de glisser ce que vous voulez autour du break.

06_BreakSimulation

 

Pour l’occasion, j’ai réalisé un exemple complet avec une boucle ParallelForEach :

07_BreakSimulation

L’énumération “items” contient par défaut l’expression C# suivante “new[] { 1,2,3,4,5,6}”.

Ce qui donne à l’exécution :

08_BreakSimulation

 

Lorsqu’item à la valeur 4, le break a lieu. La solution est simple et efficace. Ai passage, on prouve bien qu’un ParallelForEach  peut être interrompu sans problèmes et sans risquer de briser la persistance si elle est utilisée (détail auquel il faut toujours faire attention quand on fait du Workflow Foundation).

A priori, si on tente de créer un service Workflow Foundation 4.5 et un contrat, l'utilisation de deux SendReply pour un Receive n'est pas supportée.

Depuis la sortie de WF4.5, cette situation a été rapportée comme connue et le Contract First n'est pas utilisable de la sorte. On recommande de n'avoir qu'une activité SendReply en dehors de l'activité If.

Dans les faits, le problème n'est pas là. C'est la commande qui sert à créer le second SendReply qui ne sait pas qu'il faut respecter un contrat.

Si on compare les deux activités SendReply on constate les points de divergences suivants :

Activité créée par le Template Code First

Activité créée via la commande « Create SendReply »

 

Pour résumer :

  • La propriété action n'est pas bonne.
  • Les paramètres du message ne sont pas bons.

Si on corrige ses deux éléments, le service respecte à nouveau le contrat. Il y a donc bien un petit bug du côté de la commande « Create SendReply ». Mais si on comprend ce bug, on peut y remédier.

Lors de l'utilisation des activités de messages de WF, il peut arriver que l'on supprime par mégarde une activité du Template :

ReceiveReply pour le Template SendAndReceiveReply

SendReply pour le Template ReceiveEndSendReply

 

Les activités ReceiveReply et SendReply devant être liées à un Send ou un Receive, elles ne sont pas disponibles seules. Si le reste du Template n'a pas été modifié, on peut facilement tout supprimer et ajouter à nouveau le Template. Dans le cas contraire, il existe heureusement une commande sur les activités Send et Receive pour ajouter l'activité manquante.

 

Create ReceiveReply pour le Template SendAndReceiveReply

Create SendReply pour le Template ReceiveEndSendReply

 

Il est même possible d'avoir plusieurs ReveiceReply ou SendReply.

Exemple pour un ReceiveAndSendReply :

Seule ombre au tableau pour WF4.5 : ces activités ne respectent pas les contrats dans le cas d'un Contract First (ce sera l'objet d'un prochain article)

Microsoft a poussé une extension pour Visual Studio 2013 afin d'ajouter le support des projets d'installation (type de projet perdu dans Visual Studio 2012 en beta, puis réapparu et à nouveau perdu dans 2013). Pour le moment, il s'agit d'une preview. On peut croiser les doigts pour que la version finale pointe son nez rapidement.

L'annonce se trouve ici.

L'extension ici.

Petite annonce, qui semble-t-il n'a pas fait grand bruit, mais qui a tant d'importance pour ceux qui ont beaucoup d'anciens projets à maintenir.

En gros accros de la documentation, j'ai découvert il y a peu une fonctionnalité absolument géniale de la MSDN : la possibilité d'exporter un contenu sous forme de PDF ou de fichier HTML.

Dit comme cela, ça ne semble pas extraordinaire. Dans les faits :

  1. Il faut commencer la navigation via la page suivante pour la version française :

    http://msdn.microsoft.com/fr-FR/library/export/help/?returnUrl=/fr-FR/library/ms123401

    J'ai noté qu'il arrive parfois que la version française bloque lors de l'export. Je vous encourage donc à utiliser la version anglaise :

    http://msdn.microsoft.com/en-US/library/export/help/?returnUrl=/en-US/library/ms123401

     

     

  2. On click sur le bouton « Continuer à naviguer »

     

  3. Pour chaque page on peut utiliser la barre de commande « ajouter cette page » pour l'ajouter à sa sélection

     

  4. Dès que l'on a terminé ses recherches, on peut utiliser la commande « Gérer les pages »

     

  5. Ceci permet d'arriver sur la page de gestion du document

    On peut alors trier les pages, ajouter/supprimer des chapitres. Ceci permet d'organiser les documents avant l'export HTML ou PDF.

 

  1. On exporte, on télécharge, on colle dans son Windows Phone et c'est gagné ;)

 

Personnellement, j'apprécie beaucoup cette fonctionnalité pour préparer des formations.

Nul n'est censé ignorer qu'il est déconseillé d'utiliser IIS pour un site quand ce même IIS sert pour SharePoint.

Dans la pratique, un jour ou un autre, vous serez peut-être contraint de la faire. Dans le cas où l'application hébergée est compilée en 64 bits (any cpu tournant sur le pool par défaut en 64bit), il n'y a pas de problèmes (à premier abord). Dans le cas d'une application compilée en 32bits, et donc avec un pool autorisant le 32bits, les choses se corsent.

 

De base votre site ne sera pas disponible :

Cette erreur vient du fait que le pool s'est arrêté. Quand on regarde l'observateur d'évènement, l'arrêt est dû au module « SPNativeRequestModule ».

Oh la belle blague : un module 64bits lancé avec un pool 32bits… forcément, ça ne peut pas fonctionner.

 

Pour s'en sortir, il faut donc ajouter une condition pour n'activer ce module que pour les pools 64 bits (commande qui est fournie via l'aide de nombreuses applications 32bits comme Plesk):

cd %systemroot%\system32\inetsrv\

appcmd.exe set config -section:system.webServer/globalModules /[name='SPNativeRequestModule'].preCondition:integratedMode,bitness64

 

Voilà une situation qui n'est pas nouvelle, mais qui s'avère être toujours aussi embarrassante en 2014.

Si vous avez envie d'apprendre à faire des applications Windows universelles, cela tombe bien. Microsoft organise un évènement pour vous le 24 avril:

Pour s'enregistrer, ça se passe ici :

https://vts.inxpo.com/Launch/QReg.htm?ShowKey=18934&GroupID=WP

 

Voilà une belle journée de webcasts en perceptive ;)

Pour ceux qui rencontreraient des problèmes avec Visual studio 2103, ou qui se demande s'ils peuvent sauter le pas pour leurs projets existants. Sachez qu'il existe une page recensant tous les cas connus.

Visual Studio 2013 Compatibility

En plus de la liste des éléments pris en compte, il est indiqué quel comportement va avoir Visual Studio face à tel ou tel fichier (très pratique pour anticiper les fichiers qui vont être upgradés vers un nouveau format).

Parfois un me remonte des problèmes liés à la méthode ScheduleActivity() du Context.

Les problèmes sont de deux sortes :

  • L'activité ne s'exécute pas.
  • Les activités ne s'exécutent pas dans le bon ordre.

Dans la plupart des cas, il s'agit d'une incompréhension de ce que fait exactement cette méthode et du fonctionnement du scheduler.

Dans WF, il n'y a pas à proprement parler de méthode pour exécuter une activité immédiatement. On doit systématiquement passer par le Context pour lui demander de planifier l'exécution d'une activité. Notre activité ne sera donc exécutée qu'en sortie de la méthode Execute de l'activité qui demande sa planification. Cela sous-entend que l'on peut planifier plusieurs activités à partir de la même méthode Execute().

Ex :

    protected override void Execute(NativeActivityContext context)
    {
        if ((this.branches != null) && (this.Branches.Count != 0))
        {
            CompletionCallback onCompleted = new CompletionCallback(this.OnBranchComplete);
            for (int i = this.Branches.Count - 1; i >= 0; i--)
            {
                context.ScheduleActivity(this.Branches[ i ], onCompleted);
            }
        }
    }

 

Trois activités sont bien planifiées, aucune n'a encore commencé à s'exécuter en sortie de la méthode Execute. Mais il n'y a aucune garantie que les activités seront exécutées dans l'ordre Activity1, Activity2, Activity3. Il n'est même pas certain qu'elles vont s'exécuter.

Pourquoi ?

Les raisons sont toutes simples :

  • D'autres activités ou l'une des trois activités planifiées peuvent mettre fin au workflow en cours d'exécution.
  • Certaines de ces activités sont peut-être asynchrones.
  • L'hôte peut demander l'annulation du workflow en cours d'exécution.

Si on veut contrôler l'ordre d'exécution, il faut planifier les activités une à une et attendre la fin de la précédente pour lancer la suivante.

Ex : (attention, cette séquence est fournie à titre d'exemple, elle ne peut être utilisée avec la persistance, car l'index n'est pas persisté)

/// 
/// Activity based on NativeActivity
/// 
public sealed class MySequence : NativeActivity
{
    public readonly Collection m_Activities;
    private Int32 m_Index;

    private CompletionCallback m_CompletionCallback;
    private FaultCallback m_FaultCallback;

    /// 
    /// Constructeur
    /// 
    public MySequence()
    {
        this.m_Activities = new Collection();
    }

    /// 
    /// Collection d'activités de la séquence
    /// 
    public Collection Activities
    {
        get { return this.m_Activities; }
    }

    /// 
    /// Execute
    /// 
    /// WF context
    /// 
    protected override void Execute(NativeActivityContext context)
    {
        // Initialisation
        this.m_Index = 0;

        // Caching callback delegates
        this.m_CompletionCallback = new CompletionCallback(this.MyCompletionCallback);
        this.m_FaultCallback = new FaultCallback (this.MyFaultCallback);

        // Schedule next activity
        this.ScheduleNext(context); 
    }

    /// 
    /// Schedule next activity in activities collection
    /// 
    /// 
    private void ScheduleNext(NativeActivityContext context)
    {
        if (this.m_Index < this.m_Activities.Count)
        {
            context.ScheduleActivity(
                this.m_Activities[this.m_Index],
                this.m_CompletionCallback,
                this.m_FaultCallback);

            this.m_Index++;
        }
    }

    public void MyCompletionCallback(NativeActivityContext context, ActivityInstance completedInstance)
    {
        this.ScheduleNext(context);
    }

    public void MyFaultCallback(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
    {
        throw propagatedException;
    }


    /// 
    /// Register activity's metadata
    /// 
    /// 
    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        if (this.m_Activities != null
            && this.m_Activities.Count != 0)
        {
            foreach (Activity a in this.m_Activities)
            {
                metadata.AddChild(a);
            }
        }
    }
}

Avec l'arrivée de Workflow Foundation 4, le rehosting du designer a été grandement simplifié. Il n'en reste pas moins que dans certains cas, l'exercice peut devenir périlleux. Cet article n'a pas pour vocation de faire le tour des différents problèmes rencontrés, mais de vous mettre sur les bons rails pour éviter de trop nombreux écueils.

D'expérience, j'ai constaté que le choix fait par les développeurs pour charger leurs workflows dans le designer conditionnait la totalité du code qui en découlait :

  • Complexité.
  • Maintenabilité.
  • Évolutivité.
  • …et bugs…

Pour charger une définition de workflow, le WorkflowDesigner dispose d'une méthode à 3 surcharges. Si on regarde la MSDN on obtient les informations suivantes :

  • Load() : Charge un flux de travail à partir du XAML contenu dans la propriété Text.
  • Load(Object) : Charge un flux de travail à l'instance spécifiée.
  • Load(String) : Charge un flux de travail à partir du XAML contenu dans le fichier spécifié.

En réalité, les choses sont un peu plus complexes. J'ai isolé trois cas de figure qui peuvent devenir très compliqués si on choisit mal sa surcharge :

  1. Plusieurs chargements
  2. Le chargement pour l'affichage
  3. Le chargement pour un usage poussé (tracking visuel, mise à jour dynamique…)

Chacun de ses scénarios induit un usage différent.

 

1. Plusieurs chargements

Tout d'abord, la méthode Load ne peut être utilisée qu'une seule foi pour un WorkflowDesigner, quelle que soit la surcharge utilisée. Si votre application doit charger plusieurs définitions de suite, il faudra donc oublier l'idée de ne créer qu'une instance de WorkflowDesigner.

Ceci est important, car si votre WorkflowDesigner est lié à de nombreux éléments, il faut relancer le code l'initialisant avant chaque Load.

Dans le cas d'un MVVM, la bonne pratique consiste à stocker le WorkflowDesigner dans un ViewModel. Ce ViewModel aura la responsabilité de créer une nouvelle instance à chaque chargement. Simple éficace et sans problèmes, quelle que soit la surcharge du Load utilisée.

Côté gestion des ressources et performance, il faut être prudent. Même si l'idée d'avoir une instance de ce ViewModel par définition est tentante, il faut se rappeler que les WorkflowDesigner sont gourmands en RAM. Certes, le chargement d'une définition par la méthode Load, n'est pas instantané. Mais il vaut mieux avoir un petit temps de chargement (dont la perception peut être atténuée par une petite animation WPF) qu'une application lourde et peu réactivée. D'autant que l'on évite ainsi les désagréments évoqués dans le point suivant.

 

2. Le chargement pour l'affichage

Dans le cas où votre unique objectif est d'afficher une définition de workflow, les trois surcharges se valent…. Enfin presque.

La seconde surcharge n'aime pas les DynamiqueActivity. Cela induit des comportements indésirables, tels qu'un design systématiquement réduit. On se retrouve donc bien souvent avec un beau rectangle dont on ne peut rien faire. A éviter en l'état. Il est préférable de lui fournir une Activity préalablement chargée en mémoire ou un ActivityBuilder.

La première et la troisième surcharge sont préférables.

Dans certain cas, où l'interface graphique est chargée sans être visible, il peut y avoir des bugs d'affichage. Il peut être préférable de n'utiliser que la troisième surcharge. Si vos définitions de workflow proviennent d'une base de données, il faudra donc passer par un fichier temporaire. Il n'y a malheureusement pas d'approche plus propre.

 

3. Le chargement pour un usage poussé (tracking visuel, mise à jour dynamique…)

Dans ce cas, il n'y a pas de secret. La troisième surcharge est la seule qui fonctionne :

  • Pour le tracking visuel, le debug et la map ne fonctionnent qu'avec un fichier.
  • Pour les mises à jour dynamiques, certains attributs ajoutés sur une activité ne sont pas bien interprétés tant que l'activité n'est pas enregistrée dans un fichier xaml.
  • … etc …

La troisième et dernière surcharge est donc préférable pour les scénarios avancés. Même si cela induit la création de fichiers temporaires.

Après avoir rencontré quelques problèmes avec le designer dans Visual Studio et des fichiers temporaires interceptés par un abruti d'antivirus dont je ne citerai pas le nom. J'en ai conclu que ce devait être une approche similaire qui devait être utilisée dans notre IDE favori. Après chaque modification, même si l'on n'enregistre pas notre xaml, un fichier temporaire est créé.

 

Voilà pour ce petit tour d'horizon d'une méthode qui a déjà fait suer pas mal de développeurs ;)

Avec Sharepoint Foundation, cela fait déjà 3 ou 4 fois que je trouve confronté à une incohérence entre les informations fournies par le site d'administration central. Cette fois-ci, c'est une restauration déclenchée via PowerShell qui semble mal remonter :

La commande passée était :

Restore-SPFarm -Directory "\\Monserveur\Backups\" -RestoreMethod Overwrite -BackupId xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx -Force –Verbose

Et l'affichage via l'administration centrale qui indique dans le titre « qu'aucune restauration n'est en cours » et dans le détail « qu'une préparation de restauration est en cours »

 

Après investigation, la première ligne ne semble être mise à jour qu'à partir du moment où une tâche «sauvegarde/restauration » apparait dans le planificateur.

Moralité : Prudence quand vous consultez l'administration centrale.

Note : Je n'ai pas testé sur un SharePoint Server.

En voulant monter une nouvelle plateforme de démonstration Sharepoint Foundation 2013 SP1 + Office Web App 2013 SP1, je me suis retrouvé pour la 101e fois avec un problème de pré-requis :

« Fichiers Include côté serveur » et « Prise en charge du mode d'écriture manuscrite »

 

Dans le cas présent, le premier message n'est pas forcément évident. Il s'agit en fait d'une fonctionnalité qu'il faut ajouter à IIS.

 

Pour le second message, rien de bien compliqué. Il suffit d'ajouter une fonctionnalité serveur :

 

En espèrent que ce post soit utile à certains ;)

C'était une promesse du SP1 de SharePoint 2013 : « Être compatible avec Windows 2012 R2 ».

À partir d'une ISO incluant le SP1, on peut procéder normalement à l'installation des prérequis et du SharePoint Server:

 

La promesse est tenue. Seul hic, étant donné que rien n'est jamais vraiment tout rose au pays des bisounours. Aucune iso incluant SharePoint Foundation 2013 et le SP1 n'est distribuée pour le moment. Pour disposer d'un SharePoint Foundation 2013 sur Windows 2012 R2, il faudra donc encore patienter un peu.

Lors de la customisation de la MasterPage d'un SharePoint Foundation 2013, je suis tombé sur un os. Pour ne plus avoir de marges autour des pages de WebParts, et donc avoir un rendu proche de celui des wiki, j'ai codé le style suivant :

.ms-webpartPage-root { border-spacing:0 } 

Prévoyant d'autres modifications, j'ai mis le tout dans une librairie :

/Style Library/Style.css

Je voulais ajouter cette CSS à la fin du head. Sans raison apparente, mon code n'était pas pris en compte. Après plusieurs manipulations infructueuses, car non prise en charge par Sharepoint Foundation (ex :$SPurl qui a aussi besoin que l'on manipule les expressionBuilders) je suis arrivé à la solution simple et sans fioriture:

 < SharePoint:CSSRegistration name="/Style Library/Style.css" runat="server" / > 

Note : l'espace de "Style Library" est important pour que la feuille soit retrouvée.

De base, Visual Studio est malheureusement très pauvre en templates pour Workflow Foundation 4 :

  • 3 templates d'activités
  • 1 template de design.

Il y a quelques années, j'avais poussé 6 templates supplémentaires sur Skydrive dont 4 étaient aussi présents sur la galerie de Visual Studio.

Au vu du petit succès de ceux-ci, j'ai enfin pris mon courage à deux mains pour porter ces templates vers Visual Studio 2012 et 2013. Ces nouveaux templates, comme les précédents respectent les bonnes pratiques de WF4 et WF4.5. Ils comprennent donc l'écriture des metadatas.

Vous pouvez donc dès à présent en profiter via la boite à outils de gestion de vos extensions :

 

Ou si vous voulez télécharger le fichier VSIX : http://visualstudiogallery.msdn.microsoft.com/073c2a47-f666-4c84-8c7f-05b5ace26981

 

Une fois déployée, vous aurez le set de templates suivant :

 

Si tout se passe bien, je devrais publier sous peu, une extension avec des nouvelles commandes pour le designer de Workflow…

wait and see..

Pour qu'une activité expose des propriétés, deux choix s'offrent à nous :

  • Property : Une propriété CLR classique.
  • Argument : Une activité sur laquelle on définit une direction IN, OUT ou IN/OUT.

La raison pour laquelle on doit prendre l'une ou l'autre n'étant pas évidente, voici un petit récapitulatif des situations qui vous feront choisir l'une ou l'autre :

 

1. L'information est fixée lors sur design, elle ne change pas lors de l'exécution

Il faut utiliser alors une property. L'argument devant être exécuté pour fournir sa valeur, vous évitez ainsi d'utiliser inutilement trop des ressources.

 

2. L'information change lors de l'exécution

Si l'information fournie à votre activité doit être le fruit d'un travail préalable, il faut utiliser un InArgument<T> ou un InOutArgument<T>. Seuls les arguments peuvent contenir le fruit d'une Variable<T>. De la même manière, seuls les arguments peuvent interpréter une expression (Vb ou C#) lors de l'exécution.

 

3. L'information doit sortir de votre activité.

Seuls OutArgument<T> ou InOutArgument<T> peuvent sortir des valeurs de votre activité, pour que l'activité hôte de celle-ci puisse les récupérer dans une Variable<T>. On peut toujours envisager d'utiliser des ActivityFunc<T> ou ActivityAction<T>, mais ceci est vicieux et ne facilite pas la compréhension de votre activité par votre collègue (ou client) qui définit les workflows.

 

J'espère que ce petit récapitulatif vous sera utile ;)

Comment peut-on forcer une machine à changer d'état pour un état choisi?

Voilà une question qui m'est souvent posée. De base, ceci n'est pas possible « out of the box ». Certain disent qu'i il existe bien des « astuces » pour y arriver. Mais quand on y regarde bien, elles consistent presque toutes à réécrire sa propre usine à gaz pour héberger un Workflow totalement personnalisé pour l'hôte.

Pour être clair sur le sujet, pour forcer un état  il faut :

  1. Réécrire un état custom
  2. Réécrire une statemachine custom
  3. Réécrire un hôte dédié

Et à partir du moment où l'on a fait ce travail, dès que l'on voudra utiliser une fonctionnalité standard de WF, il faudra la réécrire…

Avez-vous vraiment besoin de coder votre propre Workfow Foudation, alors que Microsoft l'a déjà fait pour vous ?

Soyons claires, le titre ne reflète pas mon avis, ni même le contenu de cet article. Il ne sert qu’à attirer le chaland sur un contenu « coup de gueule ».

Depuis que je fais du .net, j’en vois et j’en entends des vertes et des pas mures sur .net et ses problèmes de performances. Dernièrement (hors contexte professionnel heureusement), j’ai été assez choqué par une situation qui ne se généralise peut-être pas encore, mais qui prend tout de même de l’ampleur : « n’utiliser que le constructeur par défaut d’une classe ».

Le framework fourmille de classes disposant de x constructeurs. Ceux-ci ne sont pas là juste pour faire beau dans le décor. Ils sont souvent destinés à une instanciation optimale.

Oui, dit comme cela, mes propos peuvent sembler étranges, et un peu old school ou totalement has been…

Cependant, quand je vois un modèle qui implémente INotifyProprtyChanged et que tout le monde utilise les accesseurs pour définir les propriétés de l’instance fraichement crée, ça me fou en rogne. Avec XAML, il ne faut donc pas s’étonner d’avoir des problèmes de performances.

Prenons une classe sans constructeurs (comme j’en vois trop) :

public class Item1 : NotifyPropertyChangedBase
{
    private Guid _id; 
    private Boolean _isRead;
    private Boolean _isStarred;

    public string Id
    {
        get { return _id; }
        set
        {
            if (_id == value) return;
            _id = value;
            RaisePropertyChanged();
        }
    }

    public Boolean IsRead
    {
        get { return _isRead; }
        set
        {
            if (_isRead == value) return;
            _isRead = value;
            RaisePropertyChanged();
        }
    }

    public Boolean IsStarred
    {
        get { return _isStarred; }
        set
        {
            if (_isStarred == value) return;
            _isStarred = value;
            RaisePropertyChanged();
        }
    }
}

Prenons une seconde classe similaire avec constructeurs :

public class Item2 : NotifyPropertyChangedBase
{
    private readonly Guid _id; 
    private Boolean _isRead;
    private Boolean _isStarred;

    public Item2() : this(Guid.NewGuid(), false, false) { }
    public Item2(Guid id) : this(id, false, false) { }

    public Item2(Guid id, bool isRead, bool isStarred)
    {
        _id = id;
        _isRead = isRead;
        _isStarred = isStarred;
    }

    public Guid Id
    {
        get { return _id; }
    }

    public Boolean IsRead
    {
        get { return _isRead; }
        set
        {
            if (_isRead == value) return;
            _isRead = value;
            RaisePropertyChanged();
        }
    }

    public Boolean IsStarred
    {
        get { return _isStarred; }
        set
        {
            if (_isStarred == value) return;
            _isStarred = value;
            RaisePropertyChanged();
        }
    }
}

L’instanciation de chacune avec les mêmes valeurs donnera ceci :

var i = new Item1{Id = id,IsRead = true, IsStarred=true};
var j = new Item2(id, true,true);

Rien de bien choquant. Si ce n’est qu’en instanciant la première classe, cela revient à écrire le code suivant :

public Item1(Guid id, bool isRead, bool isStarred)
{
    if (_id != id)
    {
        _id = id;
        RaisePropertyChanged();
    }
    if (_isRead != isRead) 
    {
        _isRead = isRead;
        RaisePropertyChanged();
    }
    if (isStarred != isStarred)
    {
        isStarred = isStarred;
        RaisePropertyChanged();
    }
}

Honnêtement, iriez-vous volontairement écrire ceci ?



Les 10 derniers blogs postés

- Créer un périphérique Windows To Go 10 ! par Blog de Jérémy Jeanson le 11-21-2014, 04:54

- RDV à Genève le 12 décembre pour l’évènement “SharePoint–Office 365 : des pratiques pour une meilleure productivité !” par Le blog de Patrick [MVP Office 365] le 11-19-2014, 10:40

- [IIS] Erreurs web personnalisées par Blog de Jérémy Jeanson le 11-19-2014, 00:00

- BDD/TDD + Javascript par Fathi Bellahcene le 11-16-2014, 16:57

- Sécuriser sans stocker de mots de passe par Blog de Jérémy Jeanson le 11-15-2014, 08:58

- Où télécharger la preview de Visual Studio 2015 ? par Blog de Jérémy Jeanson le 11-13-2014, 21:33

- Les cartes sont partout ! par Le blog de Patrick [MVP Office 365] le 11-13-2014, 17:26

- [ #Office365 ] Courrier basse priorité ! par Le blog de Patrick [MVP Office 365] le 11-12-2014, 08:56

- [Oracle] Fichier oranfsodm12.dll absent du package client par Blog de Jérémy Jeanson le 11-10-2014, 20:44

- [ #Office365 ] Le chapitre 1 des Groupes est écrit, et alors ? par Le blog de Patrick [MVP Office 365] le 11-10-2014, 20:23