Frédéric Colin
WCF : mise en place d'une transaction faisant intervenir Transactional NTFS
Windows Communication Foundation permet de mettre en place des transactions de manière simple et déclarative. Dans cet exemple, je vais vous montrer comment travailler en mode TransactionFlow et comment enrôler un autre type de Resource Manager : TxF
Par Frédéric Colin publié le 20/01/2008 à 22:07
 
Fonctionnellement parlant, il s'agira de réaliser une suppression transactionnelle d'employés en base de données, de sérialiser dans la même transaction ceux supprimés dans un fichier xml côté client et de gérer les ajouts de nouveaux employés. Le client du service sera l'instigateur de la transaction.
Voici le schéma de la table en base de données. Pour les plus attentifs, vous reconnaitrez la base de données Northwind et plus particulièrement la table Employee :
 
Table Employees

Table Employees

 
Voici la cartographie de la solution. Nous retrouvons un découpage en couche assez classique :
 
Solution Visual Studio 2008

Solution Visual Studio 2008

 
Visual Studio 2008 apporte de nombreuses nouveautés au niveau WCF et notamment l'auto-host des projets de type WCF Library. Cela permet de lancer automatiquement un processus appelé « WcfSvcHost.exe » (C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE) chargé de démarrer les écoutes actives sur tous les services.

Je n'ai pas utilisé « WcfSvcHost », car je souhaitais associer une Base de données SQL Express au processus porteur. De plus, cette solution bien que très pratique pour tester et débugger rapidement devient moins intéressante en phase de déploiement où il faudra quand même finir par développer le projet de processus porteur et y intégrer le fichier de configuration qui aura été généré dans le projet des services WCF (une bibliothèque de classe) qu'il faudra fusionner avec l'app.config (ou le web.config) du host.
L'IHM de l'application est volontairement restée simple. J'ai complètement laissé de côté l'ergonomie et la partie « Mickey » (graphisme) car cela n'était pas l'objet de l'article :
 
IHM de l'application

IHM de l'application

 
A partir de là, plusieurs étapes ont été nécessaires :
  • La mise en place du contexte transactionnel

    La transaction démarrera du côté du client par :
    1. La sérialisation en XML dans un fichier des employés à supprimer dans le répertoire d'exécution
    2. La suppression en base des employés sélectionnés pour suppression (case cochée)
    3. L'ajout en base des nouveaux employés (y compris l'image)
    Il est important de noter que je n'ai pas géré les mises à jour des employés, même si elles sont possibles via l'interface (sans sauvegarde).

    Le contexte transactionnel faisant intervenir des « Resources Managers » de types différents, il est apparu logique d'utiliser un TransactionScope pour le mettre en place. Voici donc le code afférent :
     
    Code de mise en place de la transaction

    Code de mise en place de la transaction

     
    On peut se demander à juste titre pourquoi il y a obligation d'imbriquer la partie BD dans un TransactionScope spécifique. Simplement parce que sans cela, nous obtenons un message d'erreur particulièrement abscons indiquant un problème d'enrôlement dans la transaction ambiante. La seule solution possible a été de mettre en place ce contournement. J'ai lu un post Microsoft sur le sujet que ce bug serait corrigé avec le SP1 de Vista et bien sûr sur Windows Server 2008 RTM. Qui vivra verra ...

  • La génération du proxy WCF

    Histoire de voir les améliorations Visual Studio 2008 par rapport aux extensions Orcas de Visual Studio 2005 bloquées en CTP / novembre 2006, je vous propose d'utiliser l'assistant permettant de générer automatiquement les proxies clients. Et là, tout de suite je peux vous dire que c'est pas mal du tout ! J'en veux pour preuve cette capture d'écran assez intéressante :
     
    Visual Studio 2008 : Add Service Reference

    Visual Studio 2008 : Add Service Reference

     
    La génération du proxy se charge aussi du paramétrage du fichier de configuration. J'ai simplement retouché les parties concernant la sécurité ainsi que les quotas et les tailles des messages. Ce qui nous donne :
     
    Visual Studio 2008 : Fichier de configuration généré

    Visual Studio 2008 : Fichier de configuration généré

     
    On regrettera simplement qu'à chaque mise à jour du proxy (pour répercuter les modifications sur les contrats de service par exemple), l'assistant génère de nouvelles clés pour les EndPoint, les Behaviors. Mais en y réfléchissant bien on se rend compte que cela permet d'éviter d'écraser les éventuelles modifications faites à la main !

  • La partie TxF

    Cette partie est complètement concentrée dans la classe « FileGenerationService » dont voici le diagramme :
     
    Diagramme de classe FileGenerationService

    Diagramme de classe FileGenerationService

     
    En fait, pour bien comprendre le fonctionnement, il faut comprendre que tout est basé sur l'API Win32 CreateFileTransacted(...) dont voici la définition :

    [DllImport("Kernel32.Dll", EntryPoint = "CreateFileTransacted", CharSet = CharSet.Unicode, SetLastError = true)]

    private static extern SafeFileHandle CreateFileTransacted(

           [In] String lpFileName,

           [In] SafeTransactionHandle.FileAccess dwDesiredAccess,

           [In] SafeTransactionHandle.FileShare dwShareMode,

           [In] IntPtr lpSecurityAttributes,

           [In] SafeTransactionHandle.FileMode dwCreationDisposition,

           [In] int dwFlagsAndAttributes,

           [In] IntPtr hTemplateFile,

           [In] SafeTransactionHandle txHandle,

           [In] IntPtr miniVersion,

           [In] IntPtr extendedOpenInformation

       );

    C'est cette API qui tire tout le reste ! En effet, elle nous demande de connaître ce qu'est un « SafeFileHandle », contenu dans l'espace de nom « Microsoft.Win32.SafeHandles » (c'est un wrapper vers un handle de fichier). Ensuite, en dehors de quelques paramètres simples (code hexadécimaux pour exprimer le type d'accès, le verrouillage, etc.), il ne reste plus qu'un type « inconnu » : « SafeTransactionHandle ». Il s'agit d'un wrapper vers un handle de type transactionnel utilisé par TxF offrant des services permettant de gérer (fermer, libérer) le handle sous-jacent.

    Ce « SafeTransactionHandle » tire « IKernelTransaction », permettant de récupérer une transaction au niveau du noyau par l'utilisation de la classe « TransactionInterop ». Ensuite, c'est à partir de cette référence que l'on peut récupérer ledit handle transactionnel via l'appel à la méthode « GetHandle ». C'est en dernier lieu ce handle transactionnel que l'on passera à l'appel de « CreateFileTransacted ».

    Bref, on est dans le Plateform/Invoke jusqu'au cou et même plus ! Ensuite cela, devient plus trivial car on se sert du handle obtenu au travers d'un simple StreamWriter.

    Voici l'utilisation complète :
     
    Code de la génération transactionnelle du fichier

    Code de la génération transactionnelle du fichier

     
    Cette partie de l'article est basée sur le post suivant et a un peu été revu et amélioré en fonction de ce que je souhaitais faire concrètement.
Voici à quoi ressemble l'interface du processus porteur :
 
Le processus porteur

Le processus porteur

 
Rien à dire sur le processus porteur en dehors du fait qu'en production on n'utilise pas une application console ! Le code du processus porteur est assez générique puisque je me suis débrouillé pour que l'ensemble des services déclarés dans le fichier de configuration soient chargés automatiquement, moyennant une petite astuce sur le nommage, tel que nous allons le voir.

Le fichier de configuration du processus porteur va définir deux choses : la chaine de connexion à la base et la liste des services hébergés.
 
Fichier de configuration du processus porteur

Fichier de configuration du processus porteur

 
J'avoue avoir pris goût à l'utilisation l'UAC (User Account Control) sur Vista, mais la ligne de commande qui va suivre ne va pas m'aider à vous en convaincre ! Mais bon, je suis beaucoup plus serein d'un point de vue sécurité maintenant qu'avant. Contraignez-vous à l'utiliser pendant une semaine et vous verrez que vous apprendrez plein de choses intéressantes sur Vista et la sécurisation d'un OS. L'essayez, c'est l'adopter !

J'ai donc mis en place les métadonnées sur un port différent du port 80 et il faut l'autoriser sur http pour un utilisateur d'un domaine Windows. D'où les étapes suivantes pour le faire :
  1. Ouvrir une fenêtre de commande DOS en temps qu'administrateur local
  2. Lancer la commande suivante avec les bons paramètres :

netsh http add urlacl url=http://+:80/MyUri user=DOMAIN\user

Ce qui a donné concrètement pour moi :

netsh http add urlacl url=http://+:1111/Metadata user=BEWISE\fcolin

A partir de là, il reste à lancer le host. Pour cela, il faut dans un premier temps paramétrer « DataDirectory » (cf. fichier de configuration) pour spécifier le chemin fixe au fichier mdf que je souhaitais conserver dans la racine du projet. C'est pourquoi je calcule un chemin absolu à partir du répertoire d'exécution courant et j'affecte ensuite la variable de domaine correspondante :

AppDomain.CurrentDomain.SetData("DataDirectory", System.IO.Path.GetFullPath(@"..\..\"));

Comme indiqué précédemment, j'ai souhaité mettre en place un chargement dynamique des services décrits dans le fichier de configuration. Malheureusement, la syntaxe au niveau d'un tag « service » ne permet pas de spécifier l'assembly contenant le service, ce qui est problématique pour le charger puisqu'il faut en connaitre le type pour démarrer l'écoute WCF, tout est dit. Pour résoudre cette problématique, on pouvait par exemple associer en configuration le type et l'assembly à charger pour y avoir accès. Cette solution m'ennuyait car elle supposait que l'on maintienne à jour deux configurations : l'une pour l'infrastructure WCF et l'autre pour le paramétrage des assemblies associées aux types.

J'ai donc opté pour une solution basée sur une règle de nommage : X.Y.Z où X.Y représente le nom de l'assembly (sans .dll) contenant le type et Z le nom du type qui m'intéresse et cette information, nous l'avons dans le tag « <service name=""> ». La boucle est bouclée, le reste n'est qu'un peu de parcours d'un fichier de configuration Xml et un peu réflexion !
 
Code de démarrage des hosts WCF

Code de démarrage des hosts WCF

 
Le contrat de données d'un employé a été marqué classiquement avec les attributs WCF qui vont bien (DataContract et DataMember). Mais cela n'a pas suffit du fait de l'utilisation de Linq To SQL. J'ai donc complété par l'attribut « Column » spécifique à Linq To SQL. A noter qu'il n'y a pas d'incidence sur la notion de contrat WCF en tant que tel. Il s'agit juste d'une décoration supplémentaire afin de gérer au mieux l'utilisation de Linq To SQL. Voici un échantillon du résultat obtenu :
 
Contrat de données

Contrat de données

 
Vous noterez aussi l'utilisation des propriétés auto-implémentées (pas de déclaration de membres privés) dans cette classe.
Le contrat de service d'un employé définit les services suivants :
 
Le Contrat de service

Le Contrat de service

 
Puisqu'une les classes implémentant ce service interviendront dans un flot transactionnel démarré, une session est obligatoire au niveau du host. Notez aussi, l'attribut « TransactionFlow » qui indique que le mode flot transactionnel est obligatoire (notion qui permet de s'enrôler dans une transaction qui sera démarré du côté du client du composant).
Le service implémente le contrat de service précédemment défini. Il n'y a aucune intelligence particulière dans cette implémentation du fait que toute la partie requêtage est réalisée dans une sous-couche technique. Pour les deux méthodes transactionnelles, nous définissons les comportements suivant par décoration :

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]

Une transaction doit déjà avoir été définie avant l'appel des méthodes et ces dernières valideront automatiquement la transaction dès leur sortie et si aucune exception n'a été déclenchée.

A noter la définition sur la classe d'un mode de concurrence de type « Single » n'autorisant qu'un seul thread à un instant donné, ce qui représente le comportement par défaut de WCF. Voici un exemple d'utilisation de la couche technique d'accès aux données qui se veut générique :

public void Delete(Employee emp)

{

    Sample.Data.Table<Employee> instance = new Sample.Data.Table<Employee>();

 

    instance.Delete(emp);

}

 

public List<Sample.DataContracts.Employee> GetAll(int? employeeID)

{

    Sample.Data.Table<Employee> instance = new Sample.Data.Table<Employee>();

 

    if (!employeeID.HasValue)

        return instance.GetTable();

    else

        return instance.GetTable(c => c.EmployeeID == employeeID);

}

Vous noterez l'utilisation d'une expression lambda afin de réaliser un filtrage sur l'identifiant d'un employé. Cette syntaxe est très pratique puisqu'elle permet d'avoir une couche technique complètement générique tout en permettant des conditions de filtrage spécifiques écrites de manière très simples.
Cette couche définit une classe générique unique permettant de gérer les opérations de base sur une table une base de données en s'appuyant et en encapsulant complètement Linq To SQL.
 
Diagramme de classe de la couche d'accès aux données

Diagramme de classe de la couche d'accès aux données

 
Voici le code de la classe Table<T> :
 
Code de la classe d'accès aux données – Partie 1

Code de la classe d'accès aux données – Partie 1

 
 
Code de la classe d'accès aux données – Partie 2

Code de la classe d'accès aux données – Partie 2

 
On se rend bien compte de la simplicité du requêtage qu'il soit en ajout ou en suppression. Le DataContext offre encore bien plus de fonctionnalités avec notamment la gestion des procédures stockées, les relations 1-1, 1-N.
J'ai volontairement mis de côté certains aspects dans cet article :
  • les aspects sécurité avec WCF. Bien évidemment, c'est mal mais ce n'était pas l'objet de ma prose. Histoire de ne pas vous laisser dans l'expectative, voici une référence intéressante sur les notions de sécurité avec WCF et dont je vous recommande la lecture. Il s'agit d'un article de Stéphane Goudeau intitulé « Windows Communication Foundation : Sécurité. Mise en perspective des patterns de sécurité offerts par WCF ».
  • Vous le remarquerez le code de la partie IHM n'est pas très poussée et nécessiterait quelques améliorations !
  • Je n'ai pas non plus géré les problématiques de concurrence des mises à jour et des suppressions
  • Pour tester l'application, il suffit
    • Créer de nouveaux employés
      • Lors de la mise à jour de l'image sur la bonne ligne, sélectionnez ladite ligne en cliquant dans une de ses cellules
    • Valider la transaction
    • Cocher les cases de suppression de certains nouvellement ajoutés
    • Ajouter de nouveaux employés
    • Valider la transaction
    • Vérifier la génération d'un fichier XML dans le répertoire d'exécution
    • Pour faire « planter » la transaction ambiante, il suffit de choisir un employé déjà existant en BD et référencé dans une autre table, de répéter les manipulations précédentes et de vérifier la récupération d'une exception de type contrainte référentielle et de vérifier que la génération du fichier n'a pas abouti.

 Commentaire - WCF : mise en place d'une transaction faisant intervenir Transactional NTFS 

 Dernières Publications      

Utilisation de jQuery avec ASP.NET MVC
  Développer une IHM à page unique avec ASP.NET MVC et jQuery
par Nicolas Moyère posté le 30/06/2008 à 10:28, lu 824 fois, #0
Tags: ASP.NET MVC, Ajax
Windows Media Center et WCF : développez votre maison intelligente
  Le développement d'applications pour Windows Media Center est facilité avec l'arrivée du SDK 5.3. Même si l'on sent un modèle objet bien lourd derrière, il devient plus facile d'exposer les fonctionnalités de WMC sous la forme de services WCF.
par Frédéric Colin posté le 23/06/2008 à 08:04, lu 891 fois, #0
Notions avancées avec Biztalk Server 2006 R2
  Utilisation des notions d'interchange, corrélation et convoi avec BizTalk Server 2006 R2
par Kader Yildirim posté le 09/06/2008 à 08:04, lu 705 fois, #0
Lucene Persistence Engine pour Evaluant Universal Storage Services
  Suite à l'article de Laurent Kempé, voici un moteur de stockage pour EUSS permettant l'indexation d'entités métier avec Lucene.
par Nicolas Penin posté le 01/06/2008 à 23:38, lu 1091 fois, #1
Tags: C#, Linq
XMLA Trivia : Découverte du XMLA
  Le XMLA (XML for Analysis) est un langage normalisé par plusieurs éditeurs BI pour simplifier l'accès aux données aux cubes et aux métadonnées des bases multidimensionnelles.
par Renaud Harduin posté le 25/05/2008 à 11:57, lu 1008 fois, #1
Exploiter les données CSV via Linq en toute simplicité
  A partir du requêteur dynamique fourni en exemple avec Visual Studio 2008, nous allons essayer de remplir les propriétés d'un ensemble d'objets à partir des données d'un fichier CSV. Nous enrichirons aussi le parseur de nos propres fonctions.
par Frédéric Mélantois posté le 17/05/2008 à 11:41, lu 2784 fois, #0
Comment manipuler simplement le contenu d'un fichier WordML ?
  Manipulations autour du format WordML
par Fabien Reinle posté le 14/05/2008 à 23:55, lu 1405 fois, #0
Polymorphisme et contrats de données WCF
  WCF aborde les types polymorphes du point de vue de la sérialisation. En effet, la connaissance du type réel potentiel est rendue nécessaire dès la description du contrat de données. Une fois n'est pas coutume, j'ai réalisé l'exemple en VB.NET.
par Frédéric Colin posté le 14/05/2008 à 08:40, lu 2931 fois, #2

 Dernières Actualités      

Reprise du projet Reflector par RedGate
  La nouvelle était connue depuis quelques jours par les développeurs de plugins, mais c’est désormais officiel : Lutz Roeder, le responsable de Reflector confie à la société RedGate le futur du projet....
Microsoft publie Visual Studio 2008 Service Pack 1
  Il est recommandé d’utiliser l’outil Visual Studio 2008 Service Pack preparation Tool avant de faire l’installation du Service Pack si vous avez installé des versions béta sur votre machine. Une fois que...
Tags: Framework .NET, Visual Studio 2008
Evaluant dévoile ses sources
  L'ensemble des projets R&D réalisés par les consultants de la SSII Evaluant sont en cours de publication sur CodePlex . L'objectif est de les centraliser et surtout d'augmenter leur visibilité. L'avantage...
Le Silverlight Tour en français!
  Le Silverlight Tour passe maintenant dans les pays francophones! En effet RunAtServer Consulting est partenaire du Silverlight Tour pour la gestion de cette formation Silverlight en français à commencer...
Microsoft publie ASP.NET AJAX 4.0 CodePlex Preview 1
  Cette pré-version contient les améliorations suivantes: Client-side template rendering Declarative instantiation of behaviors and controls DataView control Markup extensions Bindings Vous pouvez en lire...
Tags: Ajax
Deep Earth – Une belle utilisation de Virtual Earth et de Silverlight Deep Zoom
  Ce projet très intéressant est disponible sur Codeplex et vous pouvez voir une démo sur la page suivante . Bien entendu comme touts les projets sur Codeplex vous avez accès aux sources....
Tags: Silverlight