Etude de cas avec ASP.NET 2.0, C#, Spring.NET et NHibernate


précédentsommairesuivant

VIII. L'application [SimuPaie] - version 3 - architecture 3 couches avec NHibernate

Lectures conseillées : "Langage C# 2008, Chapitre 4 : Architectures 3 couches, tests NUnit, framework Spring".

VIII-A. Architecture générale de l'application

L'application [SimuPaie] aura maintenant la structure à trois couches suivante :

Image non disponible
  • la couche [1-dao] (dao=Data Access Object) s'occupera de l'accès aux données.
  • la couche [2-métier] s'occupera de l'aspect métier de l'application, le calcul de la paie.
  • la couche [3-ui] (ui=User Interface) s'occupera de la présentation des données à l'utilisateur et de l'exécution de ses requêtes. Nous appelons [Application] l'ensemble des modules assurant cette fonction. Elle est l'interlocuteur de l'utilisateur.
  • les trois couches seront rendues indépendantes grâce à l'utilisation d'interfaces .NET
  • l'intégration des différentes couches sera réalisée par Spring IoC

Le traitement d'une demande d'un client se déroule selon les étapes suivantes :

  • le client fait une demande à l'application.
  • l'application traite cette demande. Pour ce faire, elle peut avoir besoin de l'aide de la couche [métier] qui elle-même peut avoir besoin de la couche [dao] si des données doivent être échangées avec la base de données.
  • l'application reçoit une réponse de la couche [métier]. Selon celle-ci, elle envoie la vue (= la réponse) appropriée au client.

Prenons l'exemple du calcul de la paie d'une assistante maternelle. Celui-ci va nécessiter plusieurs étapes :

Image non disponible
  • la couche [ui] va devoir demander à l'utilisateur
  • Pour cela elle va devoir présenter à celui-ci la liste des personnes (nom, prénom, SS) présentes dans la table [EMPLOYES] afin que l'utilisateur choisisse l'une d'elles. La couche [ui] va utiliser le chemin [2, 3, 4, 5, 6, 7] pour les obtenir. L'opération [2] est la demande de la liste des employés, l'opération [7] la réponse à cette demande. Ceci fait, la couche [ui] peut présenter la liste des employés à l'utilisateur par [8].
  • l'utilisateur va transmettre à la couche [ui] le nombre de jours travaillés ainsi que le nombre d'heures travaillées. C'est l'opération [1] ci-dessus. Au cours de cette étape, l'utilisateur n'interagit qu'avec la couche [ui]. C'est celle-ci qui va notamment vérifier la validité des données saisies. Ceci fait, l'utilisateur va demander le calcul de la paie.
  • la couche [ui] va demander à la couche métier de faire ce calcul. Pour cela elle va lui transmettre les données qu'elle a reçues de l'utilisateur. C'est l'opération [2].
    la couche [metier] a besoin de certaines informations pour mener à bien son travail :

Elle va demander ces informations à la couche [dao] avec le chemin [3, 4, 5, 6]. [3] est la demande initiale et [6] la réponse à cette demande.

  • ayant toutes les données dont elle avait besoin, la couche [metier] calcule la paie de la personne choisie par l'utilisateur.
  • la couche [metier] peut maintenant répondre à la demande de la couche [ui] faite en (d). C'est le chemin [7].
  • la couche [ui] va mettre en forme ces résultats pour les présenter à l'utilisateur sous une forme appropriée puis les présenter. C'est le chemin [8].
  • on peut imaginer que ces résultats doivent être mémorisés dans un fichier ou une base de données. Cela peut être fait de façon automatique. Dans ce cas, après l'opération (f), la couche [metier] va demander à la couche [dao] d'enregistrer les résultats. Ce sera le chemin [3, 4, 5, 6]. Cela peut être fait également sur demande de l'utilisateur. Ce sera le chemin [1-8] qui sera utilisé par le cycle demande - réponse.

On voit dans cette description qu'une couche est amenée à utiliser les ressources de la couche qui est à sa droite, jamais de celle qui est à sa gauche.

Notre première implémentation de cette architecture 3 couches sera une application ASP.NET où

  • les couches [dao] et [metier] seront implémentées par des DLL
  • la couche [ui] sera implémentée par le formulaire web de la version 1 (cf page ).

Nous commençons par implémenter la couche [dao] avec le framework NHibernate.

VIII-B. La couche [dao] d'accès aux données

Image non disponible

VIII-B-1. Le projet Visual Studio C# de la couche [dao]

Le projet Visual Studio de la couche [dao] est le suivant :

Image non disponible
  • en [1], le projet dans sa globalité
  • en [2], les différentes classes du projet
  • en [3], les références du projet.
  • en [4], un dossier [lib] dans lequel ont été réunies les DLL nécessaires aux différents projets qui vont suivre

Dans les références [3] du projet, on trouve les DLL suivantes :

  • NHibernate : pour l'ORM NHibernate
  • MySql.Data : le pilote ADO.NET du SGBD MySQL
  • Spring.Core : pour le framework Spring
  • log4net : une bibliothèque de logs
  • nunit.framework : une bibliothèque de tests unitaires

Ces références ont été prises dans le dosssier [lib] [4]. On prendra soin que toutes ces références aient leur propriété "Copie locale" à "True" [5] :

Image non disponible

VIII-B-2. Les entités de la couche [dao]

Image non disponible

Les entités (objets) nécessaires à la couche [dao] ont été rassemblées dans le dossier [entites] du projet. Certaines nous sont déjà connues : [Cotisations] décrite page , [Employe] décrite page , [Indemnites] décrite page . Elles sont toutes dans l'espace de noms [Pam.Dao.Entites].

La classe [Employe] évolue de la façon suivante :

 
CacherSélectionnez

VIII-B-3. La classe [PamException]

La couche [dao] est chargée d 'échanger des données avec une source extérieure. Cet échange peut échouer. Par exemple, si les informations sont demandées à un service distant sur Internet, leur obtention échouera sur une panne quelconque du réseau. Sur ce type d'erreurs, il est classique en Java de lancer une exception. Si l'exception n'est pas de type [RunTimeException] ou dérivé, il faut indiquer dans la signature de la méthode que celle-ci lance (throws) une exception. En .NET, toutes les exceptions sont non contrôlées, c.a.d. équivalentes au type [RunTimeException] de Java. Il n'y a alors pas lieu de déclarer que les méthodes [GetAllIdentitesEmployes, GetEmploye, GetCotisations] sont susceptibles de lancer une exception.

Il est cependant intéressant de pouvoir différencier les exceptions les unes des autres car leur traitement peut différer. Ainsi le code gérant divers types d'exceptions peut être écrit de la façon suivante :

 
CacherSélectionnez

Nous créons donc un type d'exceptions pour la couche [dao] de notre application. C'est le type [PamException] suivant :

 
CacherSélectionnez
  • ligne 2 : la classe appartient à l'espace de noms [Pam.Dao.Entites]
  • ligne 4 : la classe dérive de la classe [Exception]
  • ligne 7 : elle a une propriété publique [Code] qui est un code d'erreur
  • nous utiliserons dans notre couche [dao] deux sortes de constructeur :
  • celui des lignes 18-21 qu'on peut utiliser comme montré ci-dessous :
 
CacherSélectionnez
  • ou celui des lignes 23-26 destiné à faire remonter une exception déjà survenue en l'encapsulant dans une exception de type [PamException] :
 
CacherSélectionnez

Cette seconde méthode a l'avantage de ne pas perdre l'information que peut contenir la première exception.

VIII-B-4. Les fichiers de mapping tables <--> classes de NHibernate

Revenons à l'architecture de l'application :

Image non disponible

En lecture, le framework NHibernate exploite des données de la base de données et les transforme en objets dont nous venons de présenter les classes. En écriture, il fait l'inverse : à partir d'objets, il crée, met à jour, supprime des lignes dans les tables de la base de données. Les fichiers assurant la transformation tables <--> classes ont déjà été présentés :

Image non disponible
  • le fichier [Cotisations.hbm.xml] présenté page fait la correspondance entre la table [COTISATIONS] et la classe [Cotisations]
     
    CacherSélectionnez
  • le fichier [Employe.hbm.xml] présenté page fait la correspondance entre la table [EMPLOYES] et la classe [Employe]
 
CacherSélectionnez
  • le fichier [Indemnites.hbm.xml] présenté page fait la correspondance entre la table [INDEMNITES] et la classe [Indemnites]
 
CacherSélectionnez

On notera que dans la balise <hibernate-mapping> de ces fichiers (ligne 2), on a les attributs suivants :

  • namespace : Pam.Dao.Entites. Les classes [Cotisations], [Employe] et [Indemnites] doivent se trouver dans cet espace de noms.
  • assembly : pam-dao-nhibernate. Les fichiers de mapping [*.hbm.xml] doivent être encapsulés dans une DLL nommée [pam-dao-nhibernate]. Pour obtenir ce résultat, le projet C# est configuré comme suit :Image non disponible
  • en [1], l'assembly du projet a pour nom [pam-dao-nhibernate]
  • en [2], les fichiers de mapping [*.hbm.xml] sont intégrés [3] à l'assembly du projet

VIII-B-5. L'interface [ IPamDao ] de la couche [dao]

Revenons à l'architecture de notre application :

Image non disponible

Dans les cas simples, on peut partir de la couche [metier] pour découvrir les interfaces de l'application. Pour travailler, elle a besoin de données :

  • déjà disponibles dans des fichiers, bases de données ou via le réseau. Elles sont fournies par la couche [dao].
  • pas encore disponibles. Elles sont alors fournies par la couche [ui] qui les obtient auprès de l'utilisateur de l'application.

Quelle interface doit offrir la couche [dao] à la couche [metier] ? Quelles sont les interactions possibles entre ces deux couches ? La couche [dao] doit fournir les données suivantes à la couche [metier] :

  • la liste des assistantes maternelles afin de permettre à l'utilisateur d'en choisir une en particulier
  • des informations complètes sur la personne choisie (adresse, indice…)
  • les indemnités liées à l'indice de la personne
  • les taux des différentes cotisations sociales

Ces informations sont en effet connues avant le calcul de la paie et peuvent donc être mémorisées. Dans le sens [metier] -> [dao], la couche [metier] peut demander à la couche [dao] d'enregistrer le résultat du calcul de la paie. Nous ne le ferons pas ici.

Avec ces informations, on pourrait tenter une première définition de l'interface de la couche [dao] :

 
CacherSélectionnez
  • ligne 1 : on importe l'espace de noms des entités de la couche [dao].
  • ligne 3 : la couche [dao] est dans l'espace de noms [Pam.Dao.Service]. Les éléments de l'espace de noms [Pam.Dao.Entites] peuvent être créés en plusieurs exemplaires. Les éléments de l'espace de noms [Pam.Dao.Service] sont créés en un unique exemplaire (singleton). C'est ce qui a justifié le choix des noms des espaces de noms.
  • ligne 4 : l'interface s'appelle [IPamDao]. Elle définit trois méthodes :
  • ligne 6, [GetAllIdentitesEmployes] rend un tableau d'objets de type [Employe] qui représente la liste des assistantes maternelles sous une forme simplifiée (nom, prénom, SS).
  • ligne 8, [GetEmploye] rend un objet [Employe] : l'employé qui a le n° de sécurité sociale passé en paramètre à la méthode avec les indemnités liées à son indice.
  • ligne 10, [GetCotisations] rend l'objet [Cotisations] qui encapsule les taux des différentes cotisations sociales à prélever sur le salaire brut.

VIII-C. Implémentation et tests de la couche [dao]

VIII-C-1. Le projet Visual Studio

Le projet Visual Studio a déjà été présenté. Rappelons-le :

Image non disponible
  • en [1], le projet dans sa globalité
  • en [2], les différentes classes du projet. Le dossier [entites] contient les entités manipulées par la couche [dao] ainsi que les fichiers de mapping NHibernate. Le dossier [service] contient l'interface [IPamDao] et son implémentation [PamDaoNHibernate]. Le dossier [tests] contient un test console [Main.cs] et un test unitaire [NUnit.cs].
  • en [3], les références du projet.

VIII-C-2. Le programme de test console [Main.cs]

Le programme de test [Main.cs] est exécuté dans l'architecture suivante :

Image non disponible

Il est chargé de tester les méthodes de l'interface [IPamDao]. Un exemple basique pourrait être le suivant :

 
CacherSélectionnez
  • ligne 11 : on demande à Spring une référence sur la couche [dao].
  • lignes 13-15 : test de la méthode [GetAllIdentitesEmployes] de l'interface [IPamDao]
  • ligne 18 : test de la méthode [GetEmploye] de l'interface [IPamDao]
  • ligne 21 : test de la méthode [GetCotisations] de l'interface [IPamDao]

Spring, NHibernate et log4net sont configurés par le fichier [App.config] suivant :

 
CacherSélectionnez

La configuration de NHibernate (ligne 10, lignes 25-36) a été expliquée page . On notera la ligne 34 qui indique que les fichiers de mapping se trouvent dans l'assembly [pam-dao-nhibernate]. C'est l'assembly du projet.

La configuration de Spring est faite aux lignes 6-9, 15-22. La ligne 20 définit l'objet [pamdao] utilisé par le programme console [Main.cs]. La balise <object> a ici les attributs suivants :

  • type : fixe la classe à instancier. C'est la classe [PamDaoNHibernate] qui implémente l'interface [IPamDao]. Elle sera trouvée dans la DLL [pam-dao-nhibernate] du projet.
  • init-method : la méthode de la classe [PamDaoNHibernate] à exécuter après instanciation de la classe
  • destroy-method : la méthode de la classe [PamDaoNHibernate] à exécuter lorsque le conteneur Spring est détruit à la fin de l'exécution du projet.

L'exécution faite avec la base de données décrite au paragraphe , page , donne le résultat console suivant :

 
CacherSélectionnez
  • lignes 1-2 : les 2 employés de type [Employe] avec les seules informations [SS, Nom, Prenom]
  • ligne 4 : l'employé de type [Employe] ayant le n° de sécurité sociale [254104940426058]
  • ligne 5 : les taux de cotisations

VIII-C-3. Écriture de la classe [PamDaoNHibernate]

Image non disponible

L'interface [IPamDao] implémentée par la couche [dao] est la suivante :

 
CacherSélectionnez

Question : écrire le code de la classe [PamDaoNHibernate] implémentant l'interface [IPamDao] ci-dessus à l'aide du framework NHibernate configuré tel qu'il a été présenté précédemment. On implémentera également les méthodes init et destroy exécutées par Spring. La méthode init créera la SessionFactory auprès de laquelle on obtiendra des objets Session. La méthode destroy fermera cette SessionFactory. On s'aidera des exemples du paragraphe , page .

Contraintes :

On supposera que certaines données demandées à la couche [dao] peuvent entièrement tenir en mémoire. Ainsi, pour améliorer les performances, la classe [PamDaoNHibernate] mémorisera :

  • la table [EMPLOYES] sous la forme (SS, NOM, PRENOM) nécessitée par la méthode [GetAllIdentitesEmployes] sous la forme d'un tableau d'objets de type [Employe]
  • la table [COTISATIONS] sous la forme d'un unique objet de type [Cotisations]

Cela sera fait dans la méthode [init] de la classe. Le squelette de la classe [PamDaoNHibernate] pourrait être le suivant :

 
CacherSélectionnez

VIII-C-4. Tests unitaires avec NUnit

Lectures conseillées : "Langage C# 2008, Chapitre 4 : Architectures 3 couches, tests NUnit, framework Spring".

Le test précédent avait été visuel : on vérifiait à l'écran qu'on obtenait bien les résultats attendus. C'est une méthode insuffisante en milieu professionnel. Les tests doivent toujours être automatisés au maximum et viser à ne nécessiter aucune intervention humaine. L'être humain est en effet sujet à la fatigue et sa capacité à vérifier des tests s'émousse au fil de la journée. L'outil [NUnit] aide à réaliser cette automatisation. Il est disponible à l'Url [http://www.nunit.org/].

Le projet Visual Studio de la couche [dao] va évoluer de la façon suivante :

Image non disponible
  • en [1], le programme de test [NUnit.cs]
  • en [2,3], le projet va générer une DLL nommé [pam-dao-nhibernate.dll]
  • en [4], la référence à la DLL du framework NUnit : [nunit.framework.dll]
  • en [5], la classe [Main.cs] ne sera pas incluse dans la DLL [pam-dao-nhibernate]
  • en [6], la classe [NUnit.cs] sera incluse dans la DLL [pam-dao-nhibernate]

La classe de test NUnit est la suivante :

 
CacherSélectionnez
  • ligne 11 : la classe a l'attribut [TestFixture] qui en fait une classe de test [NUnit].
  • ligne 12 : la classe dérive de la classe utilitaire AssertionHelper du framework NUnit (à partir de la version 2.4.6).
  • ligne 14 : le champ privé [pamDao] est une instance de l'interface d'accès à la couche [dao]. On notera que le type de ce champ est une interface et non une classe. Cela signifie que l'instance [pamDao] ne rend accessibles que des méthodes, celles de l'interface [IPamDao].
  • les méthodes testées dans la classe sont celles ayant l'attribut [Test]. Pour toutes ces méthodes, le processus de test est le suivant :
  • la méthode ayant l'attribut [SetUp] est tout d'abord exécutée. Elle sert à préparer les ressources (connexions réseau, connexions aux bases de données…) nécessaires au test.
  • puis la méthode à tester est exécutée
  • et enfin la méthode ayant l'attribut [TearDown] est exécutée. Elle sert généralement à libérer les ressources mobilisées par la méthode d'attribut [SetUp].
  • dans notre test, il n'y a pas de ressources à allouer avant chaque test et à désallouer ensuite. Aussi n'avons-nous pas besoin de méthode avec les attributs [SetUp] et [TearDown]. Pour l'exemple, nous avons présenté, lignes 23-26, une méthode avec l'attribut [SetUp].
  • lignes 17-20 : le constructeur de la classe initialise le champ privé [pamDao] à l'aide de Spring et [App.config].
  • lignes 29-32 : testent la méthode [GetAllIdentitesEmployes]
  • lignes 35-42 : testent la méthode [GetCotisations]
  • lignes 45-53 : testent la méthode [GetEmploye]
  • lignes 56-65 : testent la méthode [GetEmploye] lors d'une exception.

La génération du projet crée la DLL [pam-dao-nhibernate.dll] dans le dossier [bin/Release].

Image non disponible

Le dossier [bin/Release] contient en outre :

  • les DLL qui font partie des références du projet et qui ont l'attribut [Copie locale] à vrai : [Spring.Core, MySql.data, NHibernate, log4net]. Ces DLL sont accompagnées des copies des DLL qu'elles utilisent elles-mêmes :
  • [CastleDynamicProxy, Iesi.Collections] pour l'outil NHibernate
  • [antlr.runtime, Common.Logging] pour l'outil Spring
  • le fichier [pam-dao-nhibernate.dll.config] est une copie du fichier de configuration [App.config]. C'est VS qui opère cette duplication. A l'exécution c'est le fichier [pam-dao-nhibernate.dll.config] qui est utilisé et non [App.config].

On charge la DLL [pam-dao-nhibernate.dll] avec l'outil [NUnit-Gui], version 2.4.6 et on exécute les tests :

Image non disponible

Ci-dessus, les tests ont été réussis.

Travail pratique :

  • mettre en œuvre sur machine les tests de la classe [PamDaoNHibernate].
  • utiliser différents fichiers de configuration [App.config] afin d'utiliser des SGBD différents (Firebird, MySQL, Postgres, SQL Server)

VIII-C-5. Génération de la DLL de la couche [dao]

Une fois écrite et testée la classe [PamDaoNHibernate], on génèrera la DLL de la couche [dao] de la façon suivante :

Image non disponible
  • [1], les programmes de test sont exlus de l'assembly du projet
  • [2,3], configuration du projet
  • [4], génération du projet
  • la DLL est générée dans le dossier [bin/Release] [5]. Nous l'ajoutons aux DLL déjà présentes dans le dossier [lib] [6] :
Image non disponible

VIII-D. La couche métier

Revenons sur l'architecture générale de l'application [SimuPaie] :

Image non disponible

Nous considérons désormais que la couche [dao] est acquise et qu'elle a été encapsulée dans la DLL [pam-dao-nhibernate.dll]. Nous nous intéressons maintenant à la couche [metier]. C'est elle qui implémente les règles métier, ici les règles de calcul d'un salaire.

VIII-D-1. Le projet Visual Studio de la couche [metier]

Le projet Visual Studio de la couche métier pourrait ressembler à ce qui suit :

Image non disponible
  • en [1] l'ensemble du projet configuré par le fichier [App.config]
  • en [2], la couche [metier] est formée des deux dossiers [entites, service]. Le dossier [tests] contient un programme de test console (Main.cs) et un programme de test NUnit (NUnit.cs).
  • en [3] les références utilisées par le projet. On notera la DLL [pam-dao-nhibernate] de la couche [dao] étudiée précédemment.

VIII-D-2. L'interface [IPamMetier] de la couche [metier]

Revenons à l'architecture générale de l'application :

Image non disponible

Quelle interface doit offrir la couche [metier] à la couche [ui] ? Quelles sont les interactions possibles entre ces deux couches ? Rappelons-nous l'interface web qui sera présentée à l'utilisateur :

Image non disponible
  • à l'affichage initial du formulaire, on doit trouver en [1] la liste des employés. Une liste simplifiée suffit (Nom, Prénom, SS). Le n° SS est nécessaire pour avoir accès aux informations complémentaires sur l'employé sélectionné (informations 6 à 11).
  • les informations 12 à 15 sont les différents taux de cotisations.
  • les informations 16 à 19 sont les indemnités liées à l'indice de l'employé
  • les informations 20 à 24 sont les éléments du salaire calculés à partir des saisies 1 à 3 faites par l'utilisateur.

L'interface [IPamMetier] offerte à la couche [ui] par la couche [metier] doit répondre aux exigences ci-dessus. Il existe de nombreuses interfaces possibles. Nous proposons la suivante :

 
CacherSélectionnez
  • ligne 7 : la méthode qui permettra le remplissage du combo [1]
  • ligne 10 : la méthode qui permettra d'obtenir les renseignements 6 à 24. Ceux-ci ont été rassemblés dans un objet de type [FeuilleSalaire].

VIII-D-3. Les entités de la couche [metier]

Le dossier [entites] du projet Visual Studio contient les objets manipulés par la classe métier : [FeuilleSalaire] et [ElementsSalaire].

Image non disponible

La classe [FeuilleSalaire] encapsule les informations 6 à 24 du formulaire précédent :

 
CacherSélectionnez
  • ligne 8 : les informations 6 à 11 sur l'employé dont on calcule le salaire et les informations 16 à 19 sur ses indemnités. Il ne faut pas oublier ici qu'un objet [Employe] encapsule un objet [Indemnites] représentant les indemnités de l'employé.
  • ligne 9 : les informations 12 à 15
  • ligne 10 : les informations 20 à 24
  • lignes 13-15 : la méthode [ToString]

La classe [ElementsSalaire] encapsule les informations 20 à 24 du formulaire :

 
CacherSélectionnez
  • lignes 4-8 : les éléments du salaire tels qu'expliqués dans les règles métier décrites page .
  • ligne 4 : le salaire de base de l'employé, fonction du nombre d'heures travaillées
  • ligne 5 : les cotisations prélevées sur ce salaire de base
  • lignes 6 et 7 : les indemnités à ajouter au salaire de base, fonction de l'indice de l'employé et du nombre de jours travaillés
  • ligne 8 : le salaire net à payer
  • lignes 12-15 : la méthode [ToString] de la classe.

VIII-D-4. Implémentation de la couche [metier]

Image non disponible

Nous allons implémenter l'interface [IPamMetier] avec deux classes :

  • [AbstractBasePamMetier] qui est une classe abstraite dans laquelle on implémentera l'accès aux données de l'interface [IPamMetier]. Cette classe aura une référence sur la couche [dao].
  • [PamMetier] une classe dérivée de [AbstractBasePamMetier] qui elle, implémentera les règles métier de l'interface [IPamMetier]. Elle sera ignorante de la couche [dao].

La classe [AbstractBasePamMetier] sera la suivante :

 
CacherSélectionnez
  • ligne 5 : la classe appartient à l'espace de noms [Pam.Metier.Service] comme toutes les classes et interfaces de la couche [metier].
  • ligne 6 : la classe est abstraite (attribut abstract) et implémente l'interface [IPamMetier]
  • ligne 9 : la classe détient une référence sur la couche [dao] sous la forme d'une propriété publique
  • lignes 12-14 : implémentation de la méthode [GetAllIdentitesEmployes] de l'interface [IPamMetier] - utilise la méthode de même nom de la couche [dao]
  • lignes 17-19 : méthode interne (protected) [GetEmploye] qui fait appel à la méthode de même nom de la couche [dao] - déclarée protected pour que les classes dérivées puissent y avoir accès sans qu'elle soit publique.
  • lignes 22-24 : méthode interne (protected) [GetCotisations] qui fait appel à la méthode de même nom de la couche [dao]
  • ligne 27 : implémentation abstraite (attribut abstract) de la méthode [GetSalaire] de l'interface [IPamMetier].

Le calcul du salaire est implémenté par la classe [PamMetier] suivante :

 
CacherSélectionnez
  • ligne 7 : la classe dérive de [AbstractBasePamMetier] et donc implémente de ce fait l'interface [IPamMetier]
  • ligne 10 : la méthode [GetSalaire] à implémenter

Question : écrire le code de la méthode [GetSalaire].

VIII-D-5. Le test console de la couche [metier]

Rappelons le projet Visual Studio de la couche [metier] :

Image non disponible

Le programme de test [Main] ci-dessus teste les méthodes de l'interface [IPamMetier]. Un exemple basique pourrait être le suivant :

 
CacherSélectionnez
  • ligne 11 : instanciation par Spring de la couche [metier].
  • lignes 13-14 : tests de la méthode [GetSalaire] de l'interface [IPamMetier]
  • lignes 15-22 : test de la méthode [GetSalaire] lorsqu'il se produit une exception

Le programme de test utilise le fichier de configuration [App.config] suivant :

 
CacherSélectionnez

Ce fichier est identique au fichier [App.config] utilisé pour le projet de la couche [dao] (cf page ) aux détails près suivants :

  • ligne 20 : l'objet d'id "pamdao" a le type [Pam.Dao.Service.PamDaoNHibernate] et est trouvé dans l'assembly [pam-dao-nhibernate]. La couche [dao] est celle étudiée précédemment.
  • lignes 21-23 : l'objet d'id "pammetier" a le type [Pam.Metier.Service.PamMetier] et est trouvé dans l'assembly [pam-metier-dao-nhibernate]. Il faut configurer le projet en ce sens :
Image non disponible
  • ligne 22 : l'objet [PamMetier] instancié par Spring a une propriété publique [PamDao] qui est une référence sur la couche [dao]. Cette propriété est initialisée avec la référence de la couche [dao] créée ligne 20.

L'exécution faite avec la base de données décrite au paragraphe , page , donne le résultat console suivant :

 
CacherSélectionnez
  • lignes 1-2 : les 2 feuilles de salaire demandées
  • ligne 3 : l'exception de type [PamException] provoquée par un employé inexistant.

VIII-D-6. Tests unitaires de la couche métier

Le test précédent était visuel : on vérifiait à l'écran qu'on obtenait bien les résultats attendus. Nous passons maintenant aux tests non visuels NUnit.

Revenons au projet Visual Studio du projet [metier] :

Image non disponible
  • en [1], le programme de test NUnit
  • en [2], la référence sur la DLL [nunit.framework]Image non disponible
  • en [3,4], la génération du projet va produire la DLL [pam-metier-dao-nhibernate.dll].
  • en [5], le fichier [NUnit.cs] sera inclus dans l'assembly [pam-metier-dao-nhibernate.dll] mais pas [Main.cs] [6]

La classe de test NUnit est la suivante :

 
CacherSélectionnez
  • ligne 13 : le champ privé [pamMetier] est une instance de l'interface d'accès à la couche [metier]. On notera que le type de ce champ est une interface et non une classe. Cela signifie que l'instance [PamMetier] ne rend accessibles que des méthodes, celles de l'interface [IPamMetier].
  • lignes 16-19 : le constructeur de la classe initialise le champ privé [pamMetier] à l'aide de Spring et du fichier de configuration [App.config].
  • lignes 23-26 : testent la méthode [GetAllIdentitesEmployes]
  • lignes 29-42 : testent la méthode [GetSalaire]

Le projet ci-dessus génère la DLL [pam-metier.dll] dans le dossier [bin/Release].

Image non disponible

Le dossier [bin/Release] contient en outre :

  • les DLL qui font partie des références du projet et qui ont l'attribut [Copie locale] à vrai : [Spring.Core, MySql.data, NHibernate, log4net, pam-dao-nhibernate]. Ces DLL sont accompagnées des copies des DLL qu'elles utilisent elles-mêmes :
  • [CastleDynamicProxy, Iesi.Collections] pour l'outil NHibernate
  • [antlr.runtime, Common.Logging] pour l'outil Spring
  • le fichier [pam-metier-dao-nhibernate.dll.config] est une copie du fichier de configuration [App.config].

On charge la DLL [pam-metier-dao-nhibernate.dll] avec l'outil [NUnit-Gui, version 2.4.6] et on exécute les tests :

Image non disponible

Ci-dessus, les tests ont été réussis.

Travail pratique :

  • mettre en œuvre sur machine les tests de la classe [PamMetier].

VIII-D-7. Génération de la DLL de la couche [metier]

Une fois écrite et testée la classe [PamMetier], on génèrera la DLL [pam-metier-dao-nhibernate.dll] de la couche [metier] en suivant la méthode décrite au paragraphe , page . On prendra soin de ne pas inclure dans la DLL les programmes de test [Main.cs] et [NUnit.cs]. On la placera ensuite dans le dossier [lib] des DLL [1].

Image non disponible

VIII-E. La couche [web]

Revenons sur l'architecture générale de l'application [SimuPaie] :

Image non disponible

Nous considérons que les couche [dao] et [métier] sont acquises et encapsulées dans les DLL [pam-dao-nhibernate, pam-metier-dao-nhibernate.dll]. Nous décrivons mainatenant la couche web.

VIII-E-1. Le projet Visual Web Developer de la couche [web]

Image non disponible
  • en [1], le projet dans son ensemble :
  • [Global.asax] : la classe instanciée au démarrage de l'application web et qui assure l'initialisation de l'application
  • [Default.aspx] : la page du formulaire web
  • en [2], les DLL nécessaires à l'application web. On notera les DLL des couches [dao] et [metier] construites précédemment.

VIII-E-2. Configuration de l'application

Le fichier [Web.config] qui configure l'application définit les mêmes données que le fichier [App.config] configurant la couche [metier] étudiée précédemment. Celles-ci doivent prendre place dans le code prégénéré du fichier [Web.config] :

 
CacherSélectionnez

On retrouve lignes 9-12, 18-28 et 31-44 la configuration Spring et NHibernate décrite dans le fichier [App.config] de la couche [metier] (cf page ).

Global.asax.cs

 
CacherSélectionnez

On rappelle que :

  • la classe [Global.asax.cs] est instanciée au démarrage de l'application et que cette instance est accessible à toutes les requêtes de tous les utilisateurs. Les champs statiques des lignes 11-14 sont ainsi partagés entre tous les utilisateurs.
  • que la méthode [Application_Start] est exécutée une unique fois après l'instanciation de la classe. C'est la méthode où est faite en général l'initialisation de l'application.

Les données partagées par tous les utilisateurs sont les suivantes :

  • ligne 11 : le tableau d'objets de type [Employe] qui mémorisera la liste simplifiée (SS, NOM, PRENOM) de tous les employés
  • ligne 12 : une référence sur la couche [metier] encapsulée dans la DLL [pam-metier-dao-nhibernate.dll]
  • ligne 13 : un message indiquant comment s'est terminée l'initialisation (bien ou avec erreur)
  • ligne 14 : un booléen indiquant si l'initialisation s'est terminée par une erreur ou non.

Dans [Application_Start] :

  • ligne 23 : Spring instancie les couches [metier] et [dao] et rend une référence sur la couche [metier]. Celle-ci est mémorisée dans le champ statique [PamMetier] de la ligne 12.
  • ligne 25 : le tableau des employés est demandé à la couche [metier]
  • ligne 27 : le message en cas de réussite
  • ligne 32 : le message en cas d'erreur

VIII-E-3. Le formulaire [Default.aspx]

Le formulaire est celui de la version 2.

Image non disponible

Question : En vous inspirant du code C# de la page [Default.aspx.cs] de la version 2, écrire le code [Default.aspx.cs] de la version 3. L'unique différence est dans le calcul du salaire. Alors que dans la version 2, on utilisait l'API ADO.NET pour récupérer des informations dans la base de données, ici on utilisera la méthode GetSalaire de la couche [metier].

Travail pratique :

  • mettre en œuvre sur machine l'application web précédente
  • utiliser différents fichiers de configuration [Web.config] afin d'utiliser des SGBD différents (Firebird, MySQL, Postgres, SQL Server)

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2011 Serge Tahé. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.