10 novembre 2008

Afficher correctement du code dans vos blogs

Dernièrement, j'ai cherché une manière d'afficher correctement du code sur mon blog. En cherchant un petit peu, j'ai trouvé la balise HTML <pre> qui garde l'indentation des lignes. Rien qu'avec cela, le code publié est déjà beaucoup plus lisible. En voici un exemple :

using System.Collections.Generic;

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


Malheureusement, je n'ai pas trouvé de script ou d'addin fournissant une solution "clé en main" de mise en forme du code, supportant la colorisation de mots clés et l'indentation. Je vous propose donc un petit bout de code Javascript permettant de faire cela.

Voici les objectifs du script de mise en forme:

  • Rechercher toutes les balises ayant l'attribut name comportant la valeur code et appliquer la fonction formatterElement

  • La fonction formatterElement recherche l'ensemble des mots clés fournit par le tableau motCles et rajoute autour la balise <span class="motcle"> mot clé </span>

Pour installer le script sur un blog de type Blogspot, copiez les styles CSS et les fonctions Javascript suivants quelque part dans la balise head de votre modèle.

<style type="text/css">
div.code
{
background : #F0F0F0;
border: 1px solid black;
padding:5px;
margin:5px;
}
span.motcle
{
color:Blue;
}
</style>
<script type="text/javascript">
var regexpEchampement = unescape("([ \(\)\{\},\t\n\r%3C%3E\[;&])");
var motsCles = [
"public",
"private",
"protected",
"sealed",
"partial",
"static",
"class",
"struct",
"interface",
"using",
"namespace",
"object",
"string",
"int",
"bool",
"float",
"double",
"decimal",
"void",
"in",
"out",
"extern",
"true",
"false",
"new",
"if",
"for",
"foreach",
"throw",
"while",
"return",
"case",
"break",
"try",
"catch",
"finally",
"get",
"set",
"typeof",
"delegate"
];

function formatterElements(nameElement) {
var codeElements = document.getElementsByName(nameElement);
eval(unescape("for (var j = 0; j %3C codeElements.length; j++) { formatterElement(codeElements[j]); }"));
}

function formatterElement(element) {
element.className = "code";

var code = null;
if ("outerHTML" in element) {
}
else {
code = element.innerHTML;

var loop = "for ( var i = 0 ; i %3C motsCles.length ; i++) { var reg = new RegExp(regexpEchampement + \"(\" + motsCles[i] + \")\" + regexpEchampement, \"g\"); code = code.replace(reg, unescape(\"%241%3Cspan%20class%3D%27motcle%27%3E%242%3C/span%3E%243\"));}";
eval(unescape(loop));

element.innerHTML = code;
}
}
</script>


Remarquez que le tableau motsCles contient pas mal de mots clés du langage C#. Libre à vous d'ajouter/supprimer des mots clés. De même, vous pouvez aussi modifier les 2 styles CSS.

Ensuite, ajoutez ce petit bout de javascript juste avant la fermeture de la balise body (</body>):
<script type="text/javascript">
formatterElements("code");
</script>

Voila le script est installé.
Pour l'utiliser, il suffit juste d'ajouter une balise pre et une balise div ou autre avec l'attribut name (ainsi que id="code", à cause de la méthode getElementByName d'IE qui regarde uniquement l'id des éléments) contenant la valeur code. Exemple :

<pre><div name="code" id="code" ></pre>
public Impersonation(string pIdentifiant, string pMotDePasse, string pDomaine )
{
_identifiant = pIdentifiant;
_motDePasse = pMotDePasse;
_domaine = pDomaine;
}
</div></pre>

Ce qui donne :

public Impersonation(string pIdentifiant, string pMotDePasse, string pDomaine )
{
_identifiant = pIdentifiant;
_motDePasse = pMotDePasse;
_domaine = pDomaine;
}


EDIT : Pour l'instant, le script fonctionne à merveille sous Firefox, mais ne fonctionne pas vraiment sous IE 7/8. Cela vient du fait que toute affectation de la variable innerHtml d'un élément sous Internet Explorer normalise la chaine de caractères, et enlève l'ensemble tous les retours à la ligne et les espaces de plus de 2 caractères. Pour l'instant, je n'ai pas trouvé de contournement ...

6 novembre 2008

1er article sur Developpez.com

Ca y est !

Mon 1er article sur Developpez.com vient d'être publié hier.
Il traite de la génération de code dans Visual Studio 2008 et plus particulièrement de la création d'un Custom Tool. Celui-ci créé à partir d'un fichier XML (ici, un fichier de configuration Sharepoint) une classe associée. Cette dernière peut être utilisée pour récupérer facilement des GUID, chose très utile dans la programmation Sharepoint.

A noter que la classe est générée à l'aide de l'API CodeDom. Une autre alternative aurait été de générer la classe à partir d'une feuille XSLT.

En tout cas, merci à l'équipe de Developpez et surtout à Louis-Guillaume Morand, responsable de la section .Net, qui m'a aidé dans la publication et mon intégration dans l'équipe de rédaction du site.

5 novembre 2008

Droits insuffisants, pensez Impersonation !

Bien souvent, lorsque l'on développe une application web ou même parfois une application Windows (Winforms, WPF, ...), le compte Windows utilisé par l'application ne possède pas les droits suffisants pour effectuer des actions sur le système de fichier, le domaine, la base de données ou sur tout autre système.

Vous avez toujours la possibilité de modifier le compte utilisé. Par exemple, pour une application ASP.Net hébergé dans IIS, il est possible de changer le compte dans les paramètres du pool d'application. Cependant, ce n'est pas forcément une bonne solution. Cela peut même casser votre système d'authentification si celui-ci est de type Windows.

Il est aussi possible d'ajouter les droits nécessaires au compte utilisé par l'application. Mais cela est bien souvent maladroit. Le compte utilisé pour une application ASP.Net est par défaut "Service réseau". Il peut paraître indélicat d'ajouter des droits de d'ajout ou de suppression de dossier à ce compte. Une autre application web utilisant ce compte pourrait être détournée intentionnellement pour supprimer, modifier ou récupérer des données importantes ...

Une bonne solution est d'utiliser l'impersonation. Cela consiste à changer le compte utilisé pour exécuter la logique de votre application par un compte qui possède les droits nécessaires, c'est à dire un compte de service. Pour réaliser ceci, voici une petite classe utilitaire :


using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

public class Impersonation
{
[DllImport("advapi32", SetLastError = true)]
private static extern bool LogonUser(string pUserName, string pDomaine, string pMotDePasse,
int pLogonType, int pLogonProvider, out IntPtr pJeton);
[DllImport("advapi32", CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr pHandle);
private string _identifiant;
private string _motDePasse;
private string _domaine;
private WindowsImpersonationContext _impContext;

public Impersonation(string pIdentifiant, string pMotDePasse, string pDomaine )
{
_identifiant = pIdentifiant;
_motDePasse = pMotDePasse;
_domaine = pDomaine;
}

public void Impersonifier()
{
IntPtr handle = new IntPtr(0);
if (!LogonUser(_identifiant, _domaine, _motDePasse, 3, 0, out handle))
throw new Exception("Erreur d'authentification");
_impContext = new WindowsIdentity(handle).Impersonate();
CloseHandle(handle);
}

public void Fermer()
{
_impContext.Undo();
}
}


Tout est articulé autour de la fonction LogonUser qui permet de récupérer un jeton d'authentification. Elle prend entre en paramètre l'identifiant, le mot de passe et le domaine d'un compte de domaine ou local (SAM).

Voici maintenant un petit exemple d'utilisation :


try
{
Impersonation imp = new Impersonation("identifiant", "domaine.fr", "motdepasse}");
imp.Impersonifier();
try
{
// Votre code ncessitant des droits suprieurs
}
catch { }
finally
{
imp.Fermer();
}
}
catch { }

29 octobre 2008

C# 4 et Visual Studio 2010 CTP en téléchargement

A l'adresse suivante, vous trouverez une image virtuelle (Virtual PC 2007 SP1) où est installé Visual Studio 2010 !

La VM est découpée en plusieurs archives, d'un total de 8 Go. Une fois installée, l'image nécessite 75 Go d'espace libre, outch ! Mais bon, une fois passé ces petites contraintes, vous aurez l'immense privilège d'essayer C# 4 avec sa DLR, son nouveau mot clé dynamic et la covariance et controvariance sur les interfaces (mot clés in et out), et bien d'autres surprises.

Va falloir que je fasse de la place ...

Bon téléchargement.

28 octobre 2008

Contenu de la PDC 2008

La PDC 2008 (Professional Developers Conference) s'est déroulée du 25 au 28 octobre. Durant ces 4 jours intensifs, ont été présentés les produits et technologies suivantes :

C# 4.0 : La version 1.0 introduisait le code managé, la 2.0 les generiques, la 3.0 LinQ, la version 4.0 introduit la programmation dynamique. Le type des objets peut être connu juste à l'exécution, c'est à dire comme dans un langage de script (php, javascript, python, etc ...). Ceci est possible grâce à la DLR (Dynamic Language Runtime). Cela fait déjà plus d'un an que l'on en parle, et le plus célèbre des projets est l'implémentation du langage python, ironpython, dans la plateforme .Net. A l'instar de la CLR qui unifiait tous les langages de programmation statiques, la DLR unifie tous les langages dynamiques. Libre à vous de développer votre propre langage dynamique. C# 4.0 dispose d'un nouveau mot clé : dynamic. En substituant le type de votre objet par dynamic, le type de votre objet n'est connu qu'à l'exécution. dynamic est particulièrement utile pour manipuler des objets créés par des scripts. Cela permet de mélanger programmation statique et dynamique au même endroit. Le créateur du C# fait d'ailleurs une démonstration bluffante en copiant/collant un morceau de Javascript dans du C#. Après avoir modifié quelques mots clés (function, var), le code compile et fonctionne. Il est aussi possible de définir des paramètres optionnels dans les méthodes, et de les spécifier en fournissant le nom du paramètre en question. Grâce au support des types dynamiques, des paramètres optionnels et nommés, l'utilisation des objets COM est grandement simplifiée. Anders Hejlsberg part d'un exemple d'automation Excel, et simplifie petit à petit tous les appels aux objets et aux méthodes servant à piloter Excel. La covariance et controvariance a été amélioré, cela fonctionne maintenant avec les génériques de manière statique (mot clé in et out). En dernier lieu, le compilateur C# a été revu sous la forme d'un service pouvant être appelé simplement à partir d'un programme quelconque. Anders montre ainsi une console compilant et exécutant chaque ligne entrée. Le résultat est le même qu'avec un langage de script, le temps de compilation en plus bien sûr. Pour plus de détails, voici le lien de la conférence.

Visual Studio 10 : Toute l'interface de l'application est basée sur WPF. L'extensibilité a été fortement améliorée puisqu'il est possible d'intégrer des addins en copiant simplement l'assembly en question dans le répertoire prévu pour. Celui est pris en compte au prochain démarrage de l'IDE. Le designer pour Silverlight a aussi pris un coup de jeune et cela n'est pas un mal. Une gestion plus fine des fichiers de configuration a été mise en place, c'est à dire un par type de build (Debug, Release). D'ailleurs, voici l'url de la vidéo conférence.

Workflow Foundation : Une de mes briques préférées du Framework 3.0 a subit un petit lifting. Tout d'abord, l'actuel designer qui a la particularité d'être horiblement lent a été refait, ceci devant régler ce petit problème de performance. Les performances des workflows à l'exécution ont été largement améliorées. A noter aussi, la persistance des workflows est maintenant totalement personnalisable.

WPF, ASP.Net MVC, Surface SDK, Live Mesh (Bureau virtuel), Windows 7, Windows Azure : Je vous invite à aller voir le blog de Louis-Guillaume Morand pour plus de détails sur ces technologies et produits.

21 octobre 2008

Workflow Foundation : Hello World

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

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


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

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

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


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

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



public string Nom
{
get;
set;
}

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



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

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



using System;
using System.Workflow.Activities;

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

public Workflow1()
{
InitializeComponent();
}

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

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



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

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

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



AutoResetEvent waitHandle = new AutoResetEvent(false);

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

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

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



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

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

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

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

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

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


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

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

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



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

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

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



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

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


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



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

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

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

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

waitHandle.WaitOne();

Console.ReadKey();
}
}
}
}

16 octobre 2008

Indexation MOSS : Faites attention à la page par défaut d'une collection de site

Actuellement, je travaille sur un portail intranet basé sur Sharepoint MOSS. Etant en phase d'intégration, je corrige les quelques bugs restants. L'un d'entre eux m'a particulièrement surpris concernant l'indexation d'une collection de site. Celle-ci s'arrêtait au bout de 3 items "crawlés", alors que la collection contient plus de 800 éléments à indexer.

Après une petite recherche dans les logs applicatifs du serveur, je suis tombé sur une erreur de type Warning, ASP.NET 2.0.50727.0, WebEvent :

Je remarque que dans les Request Information la page d'accueil (page par défaut) de la collection de site renvoie une exception de type NullReferenceException. Pourtant, dans la définition des sources à indexer, seuls 2 sous sites ont été spécifiés. La page par défaut ne devrait pas être indexée. Pour vérifier que la page par défaut de la collection de site est en cause, j'ai donc spécifié une autre page par défaut grace à une page Layout uniquement accessible par URL : [url collection de site]/_Layouts/AreaWelcomePage.aspx.

Après avoir effectuer la manipulation et relancer le crawl, tout est rentré dans l'ordre. J'ai donc du modifier la page par défaut en éditant un script C# codé de manière inline dans la page par défaut de la collection de site.

Moralité, faites attention à la page par défaut de votre collection de sites si vous souhaitez indexer le contenu des sous sites.

8 octobre 2008

Qu'est ce qu'un workflow ?

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

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


ou un bloc :

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


pour traiter des données en boucle.

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

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

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

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

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

5 octobre 2008

Utiliser les Custom Tools dans Visual Studio

Alors déjà, qu'est ce qu'un Custom Tool ?
C'est un utilitaire sous forme de librairie (dll) qu'utilise Visual Studio afin de générer quelque chose, généralement des classes, à partir d'un fichier en entrée. Chaque fichier présent dans une solution Visual Studio (2003,2005 et 2008) propose dans sa fenêtre de propriété un champ Custom Tool.

Sur un fichier de ressources de type resx, le Custom Tool ResXFileCodeGenerator est défini d'office. Son rôle est de générer une classe contenant le code pour accéder aux différentes ressouces déclarées dans le fichier XML. Voici une liste regroupant les plus populaires :
  • MSDiscoCodeGenerator : Génère une classe proxy permettant d'accéder simplement à un web service à partir de son fichier wsdl
  • WCF Proxy Generator : A partir d'un fichier .svcmap, ce custom tool génère la classe d'accès au service
  • SettingsSingleFileGenerator et ResXFileCodeGenerator : Génère une classe proxy vers un fichier de configuration ou de ressources
  • EntityModelCodeGenerator : Génère l'ensemble des classes d'accès aux données (Entity Framework) à partir d'un fichier edmx
  • MSLinqToSQLGenerator : A l'instar de EntityModelCodeGenerator, ce custom tool s'occupe du fichier XML (.dbml) créé par le designer de LinQ to SQL.
  • MSDataSetGenerator : Créé un ensemble de fichier XML et de classes définissant un Dataset, à partir d'un fichier XSD
Les Custom Tools permettent de gagner un temps conséquent (plusieurs milliers de lignes de code peuvent être générées d'une passe) et garantissent une certaine qualité dans le code produit. Il est à noter que cela fonctionne que dans un seul sens (unidirectionnel). Modifiez un fichier généré par un Custom Tool et celui-ci sera écrasé à la prochaine génération.
Il est aussi intéressant de savoir que l'on peut redéfinir le Custom Tool attaché à n'importe quel fichier. Du coup, si le code créé par un Custom Tool ne vous plait pas, rien ne vous empêche de créer le votre et de l'attacher au fichier source. Sur le site suivant, vous trouverez une démonstration avec le WCF Proxy Generator : http://blogs.msdn.com/pedram/archive/2007/08/10/customising-wcf-proxy-generation-in-visual-studio-2008.aspx.
Alors comment savoir si vous avez besoin de créer votre propre Custom Tool ? Et bien, si vous avez un ou plusieurs fichiers XML dans vos projets C# ou VB .Net, et que vous devez créer du code pour accéder aux informations stockées dedans, un Custom Tool adapté vous permettra de vous passer de cette tâche un peu fastidieuse. Je les ai d'ailleurs utilisés pour un projet Sharepoint. Une solution typique WSS ou MOSS contient bien souvent un nombre impressionnant de fichiers XML (features, fields, ONET.XML, etc ...), et il est souvent utile de pouvoir accéder facilement à certaines de leurs informations.
Voici quelques liens si vous souhaitez créer votre propre Custom Tool :

12 mai 2008

Pourquoi développer des workflows avec Workflow Foundation ?

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


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


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


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

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