Docstoc

WCF Transaction et TxF

Document Sample
WCF  Transaction et TxF Powered By Docstoc
					               WCF : mise en place d’une transaction
               faisant intervenir Transactional NTFS




                                        Document

                                        Auteur ..........................    Frédéric Colin
                                        Email ............................   fred@bewise.fr
                                        Web .............................    www.bewise.fr
                                        Nombre de pages.........             27
                                        Date de création ..........          30/12/2007
                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




1 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    Sommaire
    1 Introduction....................................................................................................................................................................... 3
    2 Un peu de théorie ............................................................................................................................................................. 4
       2.1 « System.Transaction » .............................................................................................................................................. 4
       2.2 MSDTC en quelques mots .......................................................................................................................................... 5
       2.3 Linq To Sql .................................................................................................................................................................. 7
       2.4 Kernel Transaction Manager (KTM) ............................................................................................................................ 7
       2.5 WCF et transaction ..................................................................................................................................................... 8
    3 Présentation de la solution ............................................................................................................................................. 10
       3.1 L’IHM (Sample.Client) ............................................................................................................................................... 12
           3.1.1 La mise en place du contexte transactionnel .................................................................................................... 12
           3.1.2 La génération du proxy WCF ............................................................................................................................. 14
           3.1.3 La partie TxF ...................................................................................................................................................... 16
       3.2 Le Processus porteur (Sample.Host)......................................................................................................................... 19
       3.3 Le contrat de données (Sample.DataContracts) ....................................................................................................... 22
       3.4 Le contrat de Service (Sample.ServiceContracts) ..................................................................................................... 23
       3.5 L’implémentation du contrat de Service (Sample.Services) ..................................................................................... 23
       3.6 La couche technique d’accès aux données (Sample.Data) ....................................................................................... 24
       3.7 Remarques diverses ................................................................................................................................................. 26
    4 Conclusion ....................................................................................................................................................................... 27




                                                                                                                                                                                              Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




2 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                                1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    1 Introduction
    L’objectif de cet article est de présenter la mise en place d’une transaction simple avec Windows
    Communication Foundation faisant intervenir deux « Resources Managers » différents : une base de
    données SQL Server 2005 et le système de gestion de fichier de Vista.

    La lecture de cet article suppose quelques connaissances de base sur Windows Communication Foundation
    ainsi qu’une bonne connaissance du langage C#.

    L’exemple a été développé en C# avec Visual Studio Team System 2008, Framework 3.5 sur Windows Vista
    Ultimate US.

    Le     code   source     associé      à    cet     article                                peut         être         téléchargé            ici    :
    http://www.bewise.fr/download/articles/CodeArticle55.zip.




                                                                                                                                                         Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




3 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    2 Un peu de théorie
    Dans le monde .NET, les transactions respectent les caractéristiques de base classiques à savoir : Atomicité, Cohérence,
    Isolation et Durabilité. Au sein d’une application .NET donnée, les transactions peuvent être gérées à différent niveaux :
          Au niveau du « Resource Manager » directement (un SGBD par exemple)
          Au niveau ADO.NET
          Au niveau COM+
          En utilisant « System.Transaction »

    Etant entendu que c’est en définitive à la charge de chaque « Resource Manager » de fournir son propre mécanisme de
    compensation en cas d’échec d’une étape de la transaction pour assurer l’annulation des actions le concernant.

    Vous imaginez facilement, que plus la transaction sera pilotée proche du SGBD, plus elle sera performante et par
    conséquent … propriétaire. D’un autre côté, plus l’on s’éloigne du « Resource Manager », plus il sera facile pour la
    transaction de « s’enrôler » dans une transaction ambiante gérée à un autre niveau : via Microsoft Distributed
    Transaction Coordinator, le service MSDTC bien connu des systèmes d’exploitation Windows.

    Bref, le choix de la méthode est fonction de critères discriminants tels que :
          Besoin d’interagir avec des « Resources Managers » hétérogènes distribués ou non
          Le besoin précédent est-il ponctuel ou systématique
          Les transactions sont-elles gérées dans le corps d’une seule méthode ou réparties sur différentes méthodes
               de classes.

    Pour ma part, je choisis souvent la voie du milieu bien connue des bouddhistes : ni trop, ni trop peu. Et il est vrai
    qu’avec l’arrivée de « System.Transaction », le choix devient plus simple à faire. Pour mettre en œuvre lesdites
    transactions de cet exemple, nous utiliserons l’espace de nommage « System.Transaction » disponible depuis le
    Framework 2.0.

    2.1 « System.Transaction »
    Comme je le disais précédemment, cet espace de nom au travers de sa classe « TransactionScope » représente un bon
    compromis. En effet, il s’agit d’un système intelligent hybride qui utilise par défaut une transaction locale et qui peut
    utiliser une transaction distribuée supportée par MSDTC. Ce système de gestion de transaction est donc capable de
    promouvoir une transaction dite « simple » vers une transaction distribuée si une des conditions suivantes est remplie :
           Utilisation d’objets différents faisant intervenir de nouveaux contextes transactionnels
           Transport du flot d’exécution vers un autre « AppDomain »
           Interaction avec une un autre « Resource Manager »

    Il va de soit que le temps de promotion d’une transaction simple vers une transaction distribuée a un coût sur les
    performances générales de la transaction (le temps de la promotion justement), mais c’est le prix de la généricité dans
                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




    ce cas.

    Quelques particularités importantes :
         L’instance de la classe « TransactionScope » est souvent utilisée via un bloc « Using » chargé de la libération
             des ressources (TransactionScope est « IDisposable »)




4 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
              Dans ce cadre là, il n’est pas utile d’annuler (et impossible car la méthode n’existe pas) la transaction dans le
               bloc. Le non-appel à la méthode « Complete » et la sortie du bloc « using » suffisent
              Le TimeOut d’exécution est de 60 secondes
              Possibilité d’imbriquer les Scope transactionnels les uns avec les autres
              Possibilité d’abonnement à deux événements intéressants
                    o System.Transactions.Transaction.Current.TransactionCompleted : transaction validée
                    o System.Transactions.TransactionManager.DistributedTransactionStarted :              promotion       d’une
                          transaction simple vers une transaction distribuée. Dans ce cas, il faut bien faire attention au fait
                          que la transaction est promue après le traitement de l’événement.

    Voici un exemple d’utilisation :

    using (TransactionScope scope = new TransactionScope())
    {
        try
        {
            using (SqlConnection myCn1 = new SqlConnection("..."))
            {
                SqlCommand cmd = new SqlCommand("Insert …", myCn1);
                cmd.CommandType = CommandType.Text;
                myCn1.Open();
                cmd.ExecuteNonQuery();
            }

                using (SqlConnection myCn2 = new SqlConnection("..."))
                {
                    SqlCommand cmd = new SqlCommand("Delete …", myCn2);
                    cmd.CommandType = CommandType.Text;
                    myCn2.Open();
                    cmd.ExecuteNonQuery();
                }

              scope.Complete();
          }
          catch (Exception ex) { … }
          finally { … }
    }


    On se rend bien compte de l’apport de transparence à l’utilisation d’un « TransactionScope ». Par la suite, il est possible
    d’accéder aux propriétés de la transaction ambiante via « System.Transactions.Transaction.Current ».

    2.2 MSDTC en quelques mots
    Le fonctionnement d’un gestionnaire de ressources distribuées tel que Microsoft Distributed Transaction Coordinator
    (MSDTC) est le suivant :
          L’application appelle le Transaction Manager pour créer la transaction (gestion du contexte transactionnel, de
                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




              l’atomicité et de la durabilité)
          Tous les composants intervenants sont enrôlés dans la transaction ambiante
          Le Transaction Manager garde de manière séquentielle un log afin que la décision finale de validation ou
              d'invalidation soit durable et garantie
          Au final, si tous les RM impliqués sont prêts alors la transaction est validée et le log est effacé
          Sinon le Transaction Manager diffuse un ordre d’annulation à tous les « Resources Managers » impliqués




5 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    Il faut savoir que WCF supporte les transactions « natives » et « WS-Atomic » (standard). Ce dernier protocole définit
    comment implémenter des transactions sur la base d’un commit à deux phases :
           Phase 1
                    o Le coordinateur demande à chaque « Resource Manager » (RM) de se préparer au commit
                    o Chaque RM vote pour valider ou invalider la transaction
                    o Le coordinateur collecte les votes et prend une décision (commit ou rollback)
           Phase 2
                    o Le coordinateur demande à chaque RM de valider ou d’invalider
                    o Si un RM doit valider, il accuse réception de l’achèvement ou bien réalise le rollback
                    o Le coordinateur attend les AR des RM pour indiquer que la transaction s’est terminée correctement

    Ce mécanisme permet donc d’implémenter des transactions de type « tout ou rien » avec des systèmes faiblement
    couplés dispersés sur différentes plateformes. WCF fournit une implémentation de WS-AT construite sur MSDTC. Il est
    possible de configurer WS-AT via les propriétés du DTC. Pour cela, il faut registrer la dll suivante pour faire apparaitre
    l’onglet qui va bien !
          Regasm.exe /codebase wsatui.dll (« C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin »)




                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




6 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    2.3 Linq To Sql
    Finalement, au beau milieu de la réalisation de l’article, je me suis dit qu’il serait dommage de réaliser la couche l’accès
    aux données avec le Fx 3.5 sans utiliser Linq To SQL. Je m’y suis donc attelé afin de mettre en place 3 choses : la
    sélection éventuellement filtrée, l’ajout et la suppression.

    Linq To Sql est une implémentation d’un petit mapper objet-relationnel basé sur ADO.NET, intégré à Visual Studio 2008
    et ciblant pour l’instant exclusivement les bases SQL Server. Il permet donc de faire la liaison entre des classes et des
    tables en base de données de manière quasi transparente pour le développeur. Je dis quasi transparente, car il faudra
    quand même décrire le mapping existant soit à la main sous la forme d’un fichier de mapping ou bien avec des
    attributs, soit en utilisant le Designer intégré à Visual Studio 2008. Voici un exemple de requêtage que j’ai pu utiliser au
    travers d’une classe générique :

    public List<T> GetTable(Func<T, Boolean> predicate)
    {
        DataContext dc =
            new DataContext(
                ConfigurationManager.ConnectionStrings["NorthwindConnectionString"]
                    .ConnectionString,
                new System.Data.Linq.Mapping.AttributeMappingSource()
            );

          return (
              (from c in dc.GetTable<T>()
              select c).Where(predicate).ToList()
          );
    }
    Je reviendrai techniquement sur cette partie un peu plus loin dans cet article. Ce que j’apprécie particulièrement dans
    l’utilisation de Linq To Sql et de la syntaxe Linq en général c’est que l’on décrit précisément ce que l’on veut obtenir,
    mais pas comment faire pour l’obtenir. C’est donc l’implémentation sous-jacente de Linq qui se charge du comment
    (Linq To Object, Linq To Xml, Linq To Sql, etc.) contrairement à ce que l’on faisait avant en ouvrant précisément une
    connexion et en exécutant tel ordre SQL par exemple. L’idée d’apporter intrinsèquement et nativement au langage le
    requêtage sans passer par un Framework compliqué est brillante.

    Vous trouverez sur le Web de nombreuses explications sur l’utilisation de Linq To Sql et je ne m’étendrai pas davantage
    là-dessus. Je vous recommande donc le plan suivant pour une mise en jambe rapide :
          Le Training Kit Visual Studio 2008
          Les vidéos réalisées par Mitsuru Furuta (Microsoft) réalisé lors du Tour de France Accès aux données :
              http://www.microsoft.com/france/vision/WebcastMsdn.aspx?EID=49e4138b-0327-471e-809f-a7d83bc68908
          Les quelques articles rédigés par Scott Guthrie sur Linq To SQL et son intégration avec ASP.NET
              (http://weblogs.asp.net/scottgu/archive/tags/LINQ/default.aspx)

    Une chose était importante pour moi dans cet exemple : le fait de pouvoir utiliser mes propres définitions de contrat de
    données et non pas celles générées par le Designer Graphique de Linq To Sql. Or, le Designer Linq To SQL ne permet pas
                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




    cette séparation stricte. J’ai donc du réaliser cela à la main et j’ai choisi la décoration par attribut à la définition d’un
    fichier de mapping car plus simple à faire à la main.

    2.4 Kernel Transaction Manager (KTM)
    Non non, il ne s’agit pas d’une marque de motos bien connue des spécialistes, mais d’un service nouvellement offert
    par les plateformes Windows Vista et bientôt Windows Server 2008 seules. Il permet de rendre les transactions




7 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    disponibles au niveau des composants systèmes tels que NTFS (Transactional NTFS ou TxF) et la base de registres
    (Transactional Registry ou TxR).

    Le KTM est aussi dépendant d’une autre API Système, le Common Log File System (CLFS) assurant la gestion des logs,
    rendant ainsi possible une gestion transactionnelle. Dans le cadre de cet article, nous travaillerons seulement sur TxF.
    TxF permet ainsi aux opérations de gestion de fichiers sur une partition NTFS de participer à une transaction ambiante,
    qu’elle soit distribuée ou non. Par exemple :
          Mise à jour d’un fichier (contenu et attributs)
          La création d’un fichier et l’insertion en base de données
          La création de fichiers sur plusieurs machines
          Participation à une transaction avec plusieurs autres Resource Managers transactionnels (SGBD, MSMQ)
          Scénario supporté : un écrivain – plusieurs lecteurs concurrents et consistant. Les lecteurs transactionnels
              voient toujours une version consistante du fichier accédé. Attention, il n’y a pas de support pour les mises à
              jour concurrentes sur différentes transactions. La conséquence est que TxF n’est pas la solution adéquate
              pour les scénarios avec des utilisateurs multiples.

    Comme il s’agit d’une API que je qualifierai de « bas niveau », cela nécessite de manipuler des structures et des
    fonctions C. Nous ferons alors beaucoup de P/Invoke. Voici la logique de l’utilisation :
          Création de la transaction ambiante via un TransactionScope
          Récupération d’un « Transacted File Handle » en écriture en lieu et place d’un simple handle
          TxF verrouille l’accès
                   o Toute opération de modification sur le fichier en dehors de cette transaction recevra une erreur
                   o Tout lecteur transactionnel sur le fichier recevra une erreur à l‘ouverture dès lors que le fichier est
                        ouvert dans un cadre non transactionnel. Ce tableau issu de la documentation du MSDN résume
                        bien les verrouillages :

                                                            Transacted                                 Non-Transacted
                                                            Reader           Reader/Writer             Reader       Writer
    Transacted Reader                                       Yes              Yes                       Yes          No
    Transacted Reader/Writer                                Yes              No                        Yes          No
    Non-Transacted Reader                                   Yes              Yes                       Yes          Yes
    Non-Transacted Reader/Writer                            No               No                        Yes          yes

                     o   En d’autres termes, TxF fournit un niveau d’isolation de type « Read-Committed ». Cela signifie
                          qu’en mode transactionnel :
                                Un écrivain unique verra la version la plus récente ainsi que tous les changements réalisés
                                    dans le cadre de cette transaction.
                                Un lecteur verra seulement la dernière version validée qui existait à l’ouverture du fichier.
                                    Le lecteur est donc complètement isolé des modifications des écrivains transactionnels.
              Travail sur le handle transactionnel
          
                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




               Fermeture du handle transactionnel (avant de faire le commit)
              Validation / invalidation de la transaction ambiante

    2.5 WCF et transaction
    WCF utilise des « Policy Assertions » afin de contrôler et paramétrer le flot transactionnel. Ces dernières sont spécifiées
    par des attributs à la fois sur les contrats et les services, mais aussi par configuration.




8 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    Les transactions peuvent être initiées par le client ou par le service accédé. Dans le cas d’une transaction initiée par le
    client, nous utiliserons l’attribut « TransactionFlow » sur le contrat de service afin de préciser la capacité d’un service à
    accepter une transaction provenant d’un client et à se paramétrer en conséquence. Bien entendu, cette notion n’est
    pas applicable aux méthodes « OneWay » du fait de leurs natures asynchrones.

    [OperationContract()]
    [TransactionFlow(TransactionFlowOption.Mandatory)]
    void Debiter(Int32 NumCompte, Decimal montant);


    D’autres paramètres viennent influencer le comportement transaction d’un service. L’attribut « ServiceBehavior »
    applicable sur la classe qui implémente les méthodes d’un contrat de service possède des propriétés intéressantes :
          TransactionAutoCompleteOnSessionClose » (false)
                    o Toute transaction incomplète sera complétée quand la session fermera. Si true, le channel entrant
                         devra gérer les sessions
          « ReleaseServiceInstanceOnTransactionComplete » (true)
                    o Précise si l’instance du service doit être libérée lorsque la transaction est complétée. Si true, tout
                         nouveau message entraine la création d’une nouvelle instance
          « TransactionIsolationLevel » (Unspecified)
                    o Spécifie le niveau d’isolation de la transaction au sein du service
          « TransactionTimeout » (60 secondes)
                    o Durée maximale pendant laquelle la nouvelle transaction doit être complétée

    Enfin, l’attribut « OperationBehavior », applicable sur l’implémentation d’une méthode en particulier, permet aussi de
    paramétrer une transaction via les propriétés :
          Propriété « TransactionScopeRequired » (false)
                    o Indique si l’opération doit s’exécuter au sein d’une transaction
          Propriété « TransactionAutoComplete » (true)
                    o Auto-commit de la transaction si l’opération s’exécute avec succès

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public int AddNewEmployee(Sample.DataContracts.Employee employee)


    Maintenant que nous avons succinctement passé en revue l’ensemble des technologies utilisées dans cet article,
    analysons un peu le code de la solution.


                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




9 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    3 Présentation de la solution
    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 :




                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




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




10 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
                                                                                             IHM de l’application


                                                                                                                           Proxy généré automatiquement


                                                                                                                           Classe chargée de générer le
                                                                                                                           fichier en local et utilisant TxF


                                                                                             Couche basse dédiée à l’accès aux données


                                                                                                      Classe générique chargée de réaliser les
                                                                                                      requêtages    techniques      et   encapsulant
                                                                                                      complètement la partie Linq To Sql


                                                                                             Contrats de données échangés entre
                                                                                             les clients et les services WCF


                                                                                             Processus porteur chargé de répondre
                                                                                             aux sollicitations clientes


                                                                                                                  Base de données locale qui sera utilisée
                                                                                                                  et attachée à SQL Server Express


                                                                                             Contrats de services



                                                                                             Implémentation des services



                                                                                                                           Implémentation du métier de
                                                                                                                           gestion des employés




    Visual Studio 2008 apporte de nombreuses nouveautés au niveau WCF et notamment l’auto-host des projets de type
                                                                                                                                                               Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




    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




11 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    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.

    3.1 L’IHM (Sample.Client)
    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 :


                                      Lancement des opérations                   de                                           Présence d’un menu contextuel
                                      manière transactionnelle                                                                pour rafraîchir la grille




                     Présence d’un menu contextuel sur cette colonne pour
                     modifier l’image. Utile pour les créations d’employés.


    3.1.1 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
                                                                                                                                                              Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




    (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 :




12 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
            private void ManageCommit()
            {
Requête Linq    try
permettant de   {
récupérer les       var employeesToDelete = (from DataGridViewRow row in dataGridView1.Rows
                        let ForDeletion = row.Cells["ForDeletion"] as DataGridViewCheckBoxCell
seuls                   let emp = row.DataBoundItem as DataContracts.Employee
employés    à           where !row.IsNewRow && emp.EmployeeID != 0
supprimer               && ForDeletion.Value == ForDeletion.TrueValue
                        select emp).ToList();

                         var employeesToAdd = (from DataGridViewRow row in dataGridView1.Rows
                             let emp = row.DataBoundItem as DataContracts.Employee            Mise en place de la transaction
                             where emp != null && emp.EmployeeID == 0
Requête Linq                                                                                  principale
                             select emp).ToList();
permettant de
récupérer les            using (TransactionScope scope =
                             new TransactionScope(TransactionScopeOption.RequiresNew))                                                     Abonnement à l’événement de
seuls
                         {                                                                                                                 fin de transaction (qu’elle soit
employés    à                System.Transactions.Transaction.Current.TransactionCompleted                                                  réussie ou pas)
ajouter                          += new System.Transactions.TransactionCompletedEventHandler
                                      (Current_TransactionCompleted);
                                                                                                                                            Utilisation    de    la   classe
                               FileGenerationService instanceFileGenerationService                                                          encapsulant la gestion TxF pour
                                   = new FileGenerationService();                                                                           générer un fichier

                               if (employeesToDelete.Count > 0)
                                   instanceFileGenerationService.Generate(employeesToDelete);
                                                                                                                                            Création d’une sous-transaction
                               using (TransactionScope tsDB =                                   pour la partie BD
                                   new TransactionScope(TransactionScopeOption.RequiresNew))
                               {
                                   using (WcfProxies.EmployeeServiceClient instanceEmployeeService
                                           = new Sample.Client.WcfProxies.EmployeeServiceClient())
                                   {                                                                   Utilisation d’une instance
                                       foreach (var emp in employeesToDelete)                          du proxy WCF généré pour
                                           instanceEmployeeService.Delete(emp);                        la      suppression    des
                                                                                                                                                     employés
                                            foreach (var emp in employeesToAdd)
                                                emp.EmployeeID = instanceEmployeeService.AddNewEmployee(emp);
 Rechargement                        }
 de la grille de                                                                                                                                    Utilisation d’une instance
                                     tsDB.Complete();
 données                       }                                                                                                                    du proxy WCF généré pour
                                                                                              Validation de la sous-
                                                                                              transaction spécifique aux                            l’ajout des employés
                               scope.Complete();
                                                                                              actions BD
                         }

                       ReloadDatagrid();
                                                                 Validation      de                                          la
                                                                                                                                                                            Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




                   }
                   catch (Exception ex)                          Transaction principale
                   {
                       MessageBox.Show(ex.ToString(), "Error!");
                   }
             }




         13 / 27
         WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                                   1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    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 …

    3.1.2 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 :




                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




14 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
                                                        Implémentation d’un pattern permettant des
                                                        appels asynchrones




                                                    Génération de contrat de type                                             Choix des types         pour la
                                                    MessageContract                                                           désérialisation des    collections
                                                                                                                              d’entités




                                                                                                          Permet de réutiliser des types d’entités déjà
                                                                                                          définis dans des assemblies de type
                                                                                                          DataContract. Cela permet enfin de scinder le
                                                                                                          proxy généré des types échangés. C’est une
                                                                                                          excellente initiative de Microsoft !




                                                                                                                                                                   Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




                                 Génération des proxies Web Service utilisant
                                 l’ancienne infrastructure ASP.NET

    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 :




15 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
                                                                                Indication de mise en place d’un flot
                                                                                transactionnel.




                                                                                    Modifications au niveau des quotas et des
                                                                                    tailles des messages




    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 !

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




                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




16 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
                                                                                                     Méthode principale de génération du fichier




                                                                                                            Méthode permettant de récupérer un handle
                                                                                                            « classique »  à  partir   dans    handle
                                                                                                            transactionnel




                                                                                                             Interface permettant de récupérer une
                                                                                                             référence par interop vers la transaction DTC
                                                                                                             en cours



                                                                                                             Type handle transactionnel à partir duquel on
                                                                                                             pourra récupérer un handle dont on pourra se
                                                                                                             servir pour écrire dans le flux fichier généré




    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,
                                                                                                                                                              Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




         [In] int dwFlagsAndAttributes,
         [In] IntPtr hTemplateFile,
         [In] SafeTransactionHandle txHandle,
         [In] IntPtr miniVersion,
         [In] IntPtr extendedOpenInformation
    );




17 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    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 :
    internal void Generate(String value, string pathWithoutFileName)
    {
        SafeTransactionHandle txHandle = null;
                                                               Récupération d’un Handle Transactionnel issu
        SafeFileHandle fileHandle = null;
                                                                                                  du kernel Windows à partir de la transaction
          try                                                                                     ambiante
          {
                IKernelTransaction kernelTx =
                     (IKernelTransaction)TransactionInterop.GetDtcTransaction(Transaction.Current);

                kernelTx.GetHandle(out txHandle);                                                              Récupération d’un handle « simple » dont on
                                                                                                               va se servir pour écrire des données textuelles
                fileHandle
                    = CreateFileTransacted(
                        String.Format(@"{0}\{1}.xml"
                               , pathWithoutFileName, Guid.NewGuid().ToString())
                               , SafeTransactionHandle.FileAccess.GENERIC_WRITE
                               , SafeTransactionHandle.FileShare.FILE_SHARE_NONE
                               , IntPtr.Zero
                               , SafeTransactionHandle.FileMode.CREATE_ALWAYS
                               , 0
                               , IntPtr.Zero
                               , txHandle
                               , IntPtr.Zero
                               , IntPtr.Zero);

                if (fileHandle.IsInvalid)
                {
                    throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
                }

                using (System.IO.StreamWriter stream = new StreamWriter(
                                                                                                                                                                 Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




                    new System.IO.FileStream(fileHandle, System.IO.FileAccess.Write)
                    , Encoding.Default))
                {
                    stream.Write(value);                           Ecriture sur le handle de fichier.
                }
          }
          finally
          {




18 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
                                                                                       Fermeture de tous les handles manipulés. Pour une
                                                                                       bonne cohérence, il est très important de le faire
                                                                                       avant le « Complete » sur la transaction ambiante



                if (fileHandle != null && !fileHandle.IsInvalid )
                    fileHandle.Close();

                if (txHandle != null && !txHandle.IsInvalid )
                    txHandle.Close();
          }
    }


    Cette partie de l’article est basée sur le post suivant :
    http://www.pluralsight.com/blogs/jimjohn/archive/2006/08/31/36819.aspx et a un peu été revu et
    amélioré en fonction de ce que je souhaitais faire concrètement.

    3.2 Le Processus porteur (Sample.Host)
    Voici à quoi ressemble l’interface du 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.
                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




19 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    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.
                                                                            Instance utilisateur d’une BD SQL Express.
                                                                            Notez le paramétrage du répertoire contenant
                                                                            le fichier mdf




                                                                                                        Mise à disposition des métadonnées d’un
                                                                                                        service sur une url donnée




                                                                                               Jamais à true en production !




                                                                                                     Paramétrage des quotas et des tailles maximales des
                                                                                                     messages en entrée des services (rappelez-vous qu’un
                                                                                                     employé possède une photo de type bmp)

                                                                                                     Indication du support du flot transactionnel puisque
                                                                                                     dans notre cas la transaction démarre du côté du client




                                                                                                            Définition du service mis à disposition et du
                                                                                                            EndPoint associé


    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
                                                                                                                                                            Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




    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




20 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    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 !

    private static void StartHosts()
                                                                                                                         Lecture du fichier de configuration de
    {
        Configuration config =                                                                                           l’assembly porteuse de l’application et
            ConfigurationManager.OpenExeConfiguration                                                                    récupération      de     la     section
                (System.Reflection.Assembly.GetEntryAssembly().Location);                                                « system.serviceModel » correspondant
                                                                                                                         au paramétrage WCF
          foreach (ServiceElement element in
              (((ServiceModelSectionGroup)config.GetSectionGroup
                              ("system.serviceModel")).Services.Services))
          {                                                                                                                        On constitue une chaine de
              CreateNewHost(element.Name);                                                                                         type    « NomDeLaClasse,
          }                                                                                                                        NomDeLAssembly »
    }

    private static void CreateNewHost(String name)
    {
        // Rules to get assembly file name since "Class Name, Assembly Name"
        // is not supported by wcf service tag
        Type t = Type.GetType(name + ", " + name.Remove(name.LastIndexOf(".")));

          if (t == null)
          {
                                                                                                                                                                Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




              Console.WriteLine
                  ("Unknown type {0} in system.serviceModel configuration file section", name);

                return;
          }

          ServiceHost sh = new ServiceHost(t);




21 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
                                                                                                            Démarrage du host et de l’écoute des demandes
                                                                                                            faites par les clients pour le type donné. L’instance
                                                                                                            du host est stockée dans une liste générique.

                  sh.Open();
                  Console.WriteLine("Service {0} started successfully at {1}",
                                      t.ToString(),
                                      DateTime.Now.ToLongTimeString());
                  lstHosts.Add(sh);
             }


             3.3 Le contrat de données (Sample.DataContracts)
             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 :

                  [Table(Name="Employees")]
                  [DataContract()]                                                                                        Indique à Linq To SQL le nom de la table
                  public class Employee                                                                                   associée à l’entité
                  {
                      [Column(IsPrimaryKey=true, IsDbGenerated=true)]
                      [DataMember()]                                                                                      Indique à Linq To SQL que cette colonne
                      public int EmployeeID                                                                               est auto-incrémentée par le SGBD et qu’il
                      {
                          get;                                                                                            n’est pas nécessaire de la passer en cas
                          set;                                                                                            d’insertion / mise à jour
                      }

                       [Column()]                                                                                         Indique à Linq To SQL que cette colonne
                       [DataMember()]
                                                                                                                          est à prendre en compte dans le
                       public String LastName
 Attributs             {                                                                                                  requêtage (on peut aussi spécifier le
Classiques                 get;                                                                                           typage SQL)
   WCF                     set;
                       }

                         …/…

                       [Column(UpdateCheck = UpdateCheck.Never)]                                                          Indique à Linq To SQL que cette colonne
                       [DataMember()]                                                                                     n’est pas à prendre en compte dans la
                       public byte[] Photo
                       {
                                                                                                                          vérification de concurrence en cas de
                           get;                                                                                           suppression, de mise à jour
                           set;
                       }

                       [Column(UpdateCheck = UpdateCheck.Never)]                                                          Indique à Linq To SQL que cette colonne
                       [DataMember()]                                                                                     n’est pas à prendre en compte dans la
                       public String Notes
                                                                                                                          vérification de concurrence en cas de
                                                                                                                                                                      Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




                       {
                           get;                                                                                           suppression, de mise à jour
                           set;
                       }
                  }




      22 / 27
      WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                                1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    Vous noterez aussi l’utilisation des propriétés auto-implémentées (pas de déclaration de membres privés) dans cette
    classe.

    3.4 Le contrat de Service (Sample.ServiceContracts)
    Le contrat de service d’un employé définit les services suivants :



                                                                                                              Seules      « AddNewEmployee »          et
                                                                                                              « Delete » interviendront dans         une
                                                                                                              transaction démarrée côté client




          [ServiceContract(SessionMode = SessionMode.Required)]
          public interface IEmployeeService
          {
              [OperationContract()]
              [TransactionFlow(TransactionFlowOption.Mandatory)]
              int AddNewEmployee(Sample.DataContracts.Employee employee);

                [OperationContract()]
                [TransactionFlow(TransactionFlowOption.Mandatory)]
                void Delete(Sample.DataContracts.Employee employee);

                [OperationContract()]
                System.Collections.Generic.List<Sample.DataContracts.Employee>
                    GetAll(int? employeeID);
          }


    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).

    3.5 L’implémentation du contrat de Service (Sample.Services)
    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 :
                                                                                                                                                           Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




    [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.




23 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    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 :

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    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.

    3.6 La couche technique d’accès aux données (Sample.Data)
    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.
                                                                                                                   Le type de généricité attendu en
                                                                                                                   paramètre représente en fait un contrat
                                                                                                                   de données lié à une table en base




                                                                                                                    Surcharge permettant de gérer une
                                                                                                                    utilisation avec ou sans clause where
                                                                                                                                                             Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




24 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
                                                                                 Le type générique doit être une classe
                                                                                 avec un constructeur

    Voici le code de la classe Table<T> :                                                              Utilisation du DataContext Linq to SQL afin de
                                                                                                       réaliser les opérations sur la base de données
    public class Table<T> where T : class, new()
    {
        public List<T> GetTable()                                 Indication sur comment ont été stockées les
        {                                                         informations de mapping Classe  Table
            DataContext dc =
                new DataContext(
                    ConfigurationManager.ConnectionStrings["NorthwindConnectionString"]
                        .ConnectionString,
                    new System.Data.Linq.Mapping.AttributeMappingSource()
                );

                return (                                                                   Requêtage simple et transformation de la
                    (from c in dc.GetTable<T>()                                            projection en liste générique
                     select c).ToList()
                );
          }
                                                                                                                   Le DataContext représente la
          public List<T> GetTable(Func<T, Boolean> predicate)              classe technique de base à
          {                                                                utiliser pour toutes les requêtes
              DataContext dc =
                  new DataContext(
                      ConfigurationManager.ConnectionStrings["NorthwindConnectionString"]
                          .ConnectionString,
                      new System.Data.Linq.Mapping.AttributeMappingSource()
                  );
                                                                                             Requêtage incluant une restriction et transformation
                return (
                    (from c in dc.GetTable<T>()                                              de la projection en liste générique. On décrit bien le
                    select c).Where(predicate).ToList()                                      quoi et pas le comment
                );
          }

          public void Delete(T instance)
          {
              DataContext dc =
                  new DataContext(
                      ConfigurationManager.ConnectionStrings["NorthwindConnectionString"]
                          .ConnectionString,
                      new System.Data.Linq.Mapping.AttributeMappingSource()
                  );

                System.Data.Linq.Table<T> t = dc.GetTable<T>();
                t.Attach(instance, false);                                                         Nécessité d’attacher l’instance sérialisée         en
                                                                                                   provenance du client et qui doit être supprimée
                t.DeleteOnSubmit(instance);

                dc.SubmitChanges();                                                                Place l’entité dans un état en attente de suppression
                                                                                                                                                           Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




          }

          public void Add(T instance)                            Permet de définir les objets à insérer, mettre à jour
          {                                                      ou supprimer et exécute la requête appropriée
              DataContext dc =
                  new DataContext(
                      ConfigurationManager.ConnectionStrings["NorthwindConnectionString"]
                          .ConnectionString,




25 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
                             new System.Data.Linq.Mapping.AttributeMappingSource()
                      );

                System.Data.Linq.Table<T> t = dc.GetTable<T>();
                t.InsertOnSubmit(instance);

                dc.SubmitChanges();                                            Place l’entité dans un état en attente d’insertion
          }
    }


    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.

    3.7 Remarques diverses
    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 » (http://www.netfxfactory.org/docs/wcfsecurity.pdf).
           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
                   o 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
                   o Valider la transaction
                   o Cocher les cases de suppression de certains nouvellement ajoutés
                   o Ajouter de nouveaux employés
                   o Valider la transaction
                   o Vérifier la génération d’un fichier XML dans le répertoire d’exécution
                   o 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.


                                                                                                                                                       Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




26 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                           1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr
    4 Conclusion
    J’espère au travers de cet article vous avoir fait partager quelques connaissances autour des transactions
    WCF par l’utilisation d’un Resource Manager tout nouveau et peu connu : TxF. Bien entendu, les
    applications de ce genre d’outils (lorsqu’ils seront un poil plus mâtures avec le SP1 Vista et Vindows Server
    2008 et successeurs) pourront être relativement intéressantes, pour les installations par exemple.

    Bien entendu, on est encore très loin d’une API managée permettant de gérer un Resource Manager de
    type NTFS, mais nous avons enfin des API bas niveau pour le faire et je dirai que c’est de bon augure pour
    les OS post Windows Server 2008. Il me tarde quand même cette intégration complète et managée. Mais
    comme le dit le dicton, « chi va piano va sano ! ».

    En dernier lieu, il m’est difficile de terminer cet article sans parler en deux mots des apports du Fx 3.5 sur
    l’aspect WCF tant ils sont tous intéressants ! Voici donc les principaux qui m’intéressent le plus en ce
    moment :
         1. Modèle de programmation http, Sérialisation JSON et intégration ASP.NET AJAX. Ouverture vers
             un modèle de distribution de composant plus simple que SOAP. Ces deux modèles ne sont pas si
             antagonistes que cela !
         2. Syndication : la mise en place d’un flux RSS via un service est grandement facilitée par les nouvelles
             APIs .NET fournies
         3. Support des appels à partir d’Assembly « Partially Trusted » (exemple addin Outlook)

    Tous ces points forts intéressants nécessiteraient un article pour chacun d’entre eux. Mais c’est une autre
    histoire et le temps est une chose précieuse et rare en ce moment …




                                                                                                                                                     Bewise SARL au capital de 99 869 € - SIRET : 421 750 381 0012 – APE : 722C




27 / 27
WCF : mise en place d’une transaction faisant intervenir Transactional NTFS

                         1, avenue de l’Europe – Campus 1 – Bât F. – 31400 Toulouse – Tél. : 05 61 75 13 13 – Fax : 05 61 75 47 97 – www.bewise.fr

				
DOCUMENT INFO
Shared By:
Categories:
Stats:
views:55
posted:5/11/2010
language:French
pages:27