Web3tier Dotnet Part1 PDF
Web3tier Dotnet Part1 PDF
Web3tier Dotnet Part1 PDF
net Partie 1
Les ides exprimes dans ce document ont pour origine un livre lu au cours de l't 2004, le magnifique travail de Rod Johnson : J2EE Development without EJB aux ditions Wrox.
web3tier-dotnet-part1, le 09/04/05
1/68
1 Introduction
Objectifs de l'article : crire une application web 3 couches [interface utilisateur, mtier, accs aux donnes] configurer l'application avec Spring IOC crire diffrentes versions en changeant l'implmentation de l'une ou l'autre des trois couches. Outils utiliss :
Visual Studio.net pour le dveloppement - voir en annexe le paragraphe 7.1, page 52. Serveur web Cassini pour l'excution - voir annexe paragraphe 7.2, page 55 Nunit pour les tests unitaires - voir annexe paragraphe 7.4, page 62 Spring pour l'intgration et la configuration des couches de l'application web - voir annexe paragraphe 7.3, page 61
Dans une chelle dbutant-intermdiaire-avanc, ce document est dans la partie [intermdiaire-avanc]. Sa comprhension ncessite divers pr-requis. Certains d'entre-eux peuvent tre acquis dans des documents que j'ai crits. Dans ce cas, je les cite. Il est bien vident que ce n'est qu'une suggestion et que le lecteur peut utiliser ses documents favoris.
langage VB.net : [http://tahe.developpez.com/dotnet/vbnet/] programmation web en VB.net : [http://tahe.developpez.com/dotnet/aspnet/vol1 et http://.../vol2] utilisation de l'aspect IoC de Spring : [http://tahe.developpez.com/dotnet/springioc] documentation Spring.net : [http://www.springframework.net/documentation.html]
Ce document reprend le fil conducteur d'un document crit pour Java [http://tahe.developpez.com/java/web3tier]. Nous construisons en VB.NET l'application web MVC trois couches crite en Java. L'ide que l'on veut faire passer ici est que les plateformes de dveloppement Java et .Net sont suffisamment proches l'une de l'autre pour que les comptences acquises dans l'un de ces deux domaines puissent tre rutilises dans l'autre. Il ne semble pas exister de solution de dveloppement MVC ASP.NET largement reconnue. La solution qui suit reprend la mthode introduite dans le document [http://tahe.developpez.com/dotnet/aspnet/vol1]. Si celle-ci a le mrite d'utiliser des concepts rpandus dans le dveloppement J2EE, il ne faut cependant la prendre que pour ce qu'elle est, c.a.d. une mthode parmi d'autres de dveloppement MVC. Ds qu'une mthode de dveloppement MVC en ASP.NET sera largement accepte, il faudra alors adopter cette dernire. La version .Net de Spring, en cours de dveloppement, pourrait bien tre une premire solution.
2 L'application webarticles
Nous prsentons ici les lments d'une application web simplifie de commerce lectronique. Celle-ci permettra des clients du web : - de consulter une liste d'articles provenant d'une base de donnes - d'en mettre certains dans un panier lectronique - de valider celui-ci. Cette validation aura pour seul effet de mettre jour, dans la base, les stocks des articles achets. Les diffrentes vues prsentes l'utilisateur seront les suivantes : - la vue "LISTE" qui prsente une liste des articles en vente - la vue [INFOS] qui donne des informations supplmentaires sur un produit :
les vues [PANIER] et [PANIERVIDE] qui donnent le contenu du panier du client 2/68
web3tier-dotnet-part1, le 09/04/05
utilisateur
Donnes
les trois couches sont rendues indpendantes grce l'utilisation d'interfaces l'intgration des diffrentes couches est ralise par Spring chaque couche fait l'objet d'espaces de noms spars : web (couche UI), domain (couche mtier) et dao (couche d'accs aux donnes).
L'application respectera une architecture MVC (Modle - Vue - Contrleur). Si nous reprenons le schma en couches ci-dessus, l'architecture MVC s'y intgre de la faon suivante :
web3tier-dotnet-part1, le 09/04/05
3/68
1
utilisateur
Contrleur 4 3 Vues
2 Modle
Donnes
SPRING
Le traitement d'une demande d'un client se droule selon les tapes suivantes : 1. le client fait une demande au contrleur. Ce contrleur sera ici une page .aspx laquelle on fera jouer un rle particulier. Elle voit passer toutes les demandes des clients. C'est la porte d'entre de l'application. C'est le C de MVC. 2. le contrleur traite cette demande. Pour ce faire, il peut avoir besoin de l'aide de la couche mtier, ce qu'on appelle le modle M dans la structure MVC. 3. le contrleur reoit une rponse de la couche mtier. La demande du client a t traite. Celle-ci peut appeler plusieurs rponses possibles. Un exemple classique est une page d'erreurs si la demande n'a pu tre traite correctement une page de confirmation sinon 4. le contrleur choisit la rponse (= vue) envoyer au client. Celle-ci est le plus souvent une page contenant des lments dynamiques. Le contrleur fournit ceux-ci la vue. 5. la vue est envoye au client. C'est le V de MVC.
4 Le modle
Nous tudions ici le M de MVC. Le modle est ici constitu des lments suivants : 1. 2. 3. les classes mtier les classes d'accs aux donnes la base de donnes
cl primaire identifiant un article de faon unique nom de l'article son prix son stock actuel le stock au-dessous duquel une commande de rapprovisionnement doit tre faite
4/68
istia.st.articles.dao : contient les classes d'accs aux donnes de la couche [dao] istia.st.articles.domain : contient les classes mtier de la couche [domain]
Chacun de ces espaces de noms sera gnr au sein d'un fichier " assembly " qui lui sera propre :
assembly
webarticles-dao
contenu
- [IArticlesDao]: l'interface d'accs la couche [dao] C'est la seule interface que voit la couche [domain]. Elle n'en voit pas d'autre. - [Article] : classe dfinissant un article - [ArticlesDaoArrayList] : classe d'implmentation de l'interface [IArticlesDao] avec une classe [ArrayList]
rle
couche d'accs aux donnes - se trouve entirement dans la couche [dao] de l'architecture 3-tier de l'application web
webarticles-domain
- [IArticlesDomain]: l'interface d'accs la couche [domain]. C'est la seule interface que voit la couche web. Elle n'en voit pas d'autre. - [AchatsArticles] : une classe implmentant [IArticlesDomain] - [Achat] : classe reprsentant l'achat d'un client - [Panier] : classe reprsentant l'ensemble des achats d'un client
reprsente le modle des achats sur le web - se trouve entirement dans la couche [domain] de l'architecture 3tier de l'application web
Commentaires :
la projet [dao] est de type [bibliothque de classes] les classes ont t mises dans une arborescence de racine le dossier [istia]. Elles sont toutes dans l'espace de noms [istia.st.articles.dao].
web3tier-dotnet-part1, le 09/04/05
5/68
6/68
' constructeur avec proprits Public Sub New(ByVal id As Integer, ByVal nom As String, ByVal prix As Double, ByVal stockactuel As Integer, ByVal stockminimum As Integer) Me.id = id Me.nom = nom Me.prix = prix Me.stockactuel = stockactuel Me.stockminimum = stockminimum End Sub ' mthode d'identification de l'article Public Overrides Function ToString() As String Return "[" + id.ToString + "," + nom + "," + prix.ToString + "," + stockactuel.ToString + "," + stockminimum.ToString + "]" End Function End Class End Namespace
Cette classe offre : 1. 2. 3. 4. un constructeur permettant de fixer les 5 informations d'un article : [id, nom, prix, stockactuel, stockminimum] des proprits publiques permettant de lire et crire les 5 informations. une vrification des donnes insres dans l'article. En cas de donnes errones, une exception est lance. une mthode toString qui permet d'obtenir la valeur d'un article sous forme de chane de caractres. C'est souvent utile pour le dbogage d'une application.
rend tous les articles de la source de donnes vide la source de donnes rend l'objet [Article] identifi par sa cl primaire permet d'ajouter un article la source de donnes permet de modidier un article de la source de donnes permet de supprimer un article de la source de donnes permet de modifier le stock d'un article de la source de donnes
L'interface met disposition des programmes clients un certain nombre de mthodes dfinies uniquement par leurs signatures. Elle ne s'occupe pas de la faon dont ces mthodes seront rellement implmentes. Cela amne de la souplesse dans une application. Le programme client fait ses appels sur une interface et non pas sur une implmentation prcise de celle-ci.
web3tier-dotnet-part1, le 09/04/05
7/68
Implmentation 1
Implmentation 2
Le choix d'une implmentation prcise se fera au moyen d'un fichier de configuration Spring. Afin de montrer que pour tester l'application web, seule l'interface d'accs aux donnes compte et non sa classe d'implmentation, nous allons tout d'abord implmenter la source de donnes par un simple objet [ArrayList]. Ultrieurement, nous prsenterons une solution base de SGBD.
8/68
End SyncLock End Function ' modifier un article Public Function modifieArticle(ByVal articleNouveau As Article) As Integer Implements IArticlesDao.modifieArticle ' on modifie un article SyncLock Me ' on vrifie qu'il existe Dim ipos As Integer = posArticle(articles, articleNouveau.id) ' le cas o il n'existe pas If ipos = -1 Then Return 0 ' il existe - on le modifie articles(ipos) = articleNouveau ' on rend le rsultat Return 1 End SyncLock End Function ' supprimer un article identifi par sa cl Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle ' suppression d'un article SyncLock Me ' on vrifie qu'il existe Dim ipos As Integer = posArticle(articles, idArticle) ' le cas o il n'existe pas If ipos = -1 Then Return 0 ' il existe - on le supprime articles.RemoveAt(ipos) ' on rend le rsultat Return 1 End SyncLock End Function ' changer le stock d'un article identifi par sa cl Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle ' changer le stock d'un article SyncLock Me ' on vrifie qu'il existe Dim ipos As Integer = posArticle(articles, idArticle) ' le cas o il n'existe pas If ipos = -1 Then Return 0 ' il existe - on modifie son stock si on peut Dim unArticle As Article = CType(articles(ipos), Article) ' on ne change le stock que s'il est suffisant If unArticle.stockactuel + mouvement >= 0 Then unArticle.stockactuel += mouvement Return 1 Else Return 0 End If End SyncLock End Function ' chercher un article identifi par sa cl Private Function posArticle(ByVal listArticles As ArrayList, ByVal idArticle As Integer) As Integer ' rend la position de l'article [idArticle] dans la liste ou -1 si pas trouv Dim unArticle As Article For i As Integer = 0 To listArticles.Count - 1 unArticle = CType(listArticles(i), Article) If unArticle.id = idArticle Then Return i End If Next ' pas trouv Return -1 End Function End Class End Namespace
Commentaires :
la source de donnes est simule par le champ priv [articles] de type [ArrayList] le constructeur de la classe cre par dfaut 4 articles dans la source de donnes. toutes les mthodes d'accs aux donnes ont t synchronises afin d'viter les problmes d'accs concurrents la source de donnes. A un moment donn, un seul thread a accs une mthode donne. la mthode [posArticle] permet de connatre la position [0..N] dans la source [ArrayList] d'un article identifi par son n. Si l'article n'existe pas, la mthode rend la position -1. Cette mthode est utillise de faon rpte par les autres mthodes. la mthode [ajouteArticle] permet d'ajouter un article dans la liste des articles. Elle rend le nombre d'articles insrs : 1. Si l'article existait dj, une exception est lance. 9/68
web3tier-dotnet-part1, le 09/04/05
la mthode [modifieArticle] permet de modifier un article existant. Elle rend le nombre d'articles modifis : 1 si l'article existait, 0 sinon. la mthode [supprimeArticle] permet de supprimer un article existant. Elle rend le nombre d'articles supprims : 1 si l'article existait, 0 sinon. la mthode [getAllArticles] donne la liste de tous les articles la mthode [getArticleById] permet d'obtenir un article identifi par son n. On obtient la valeur [nothing] si l'article n'xiste pas. le code ne prsente pas de relle difficult. Nous laissons le lecteur le parcourir et le comprendre.
web3tier-dotnet-part1, le 09/04/05
10/68
Commentaires : le projet [tests] est de type [bibliothque de classes] les tests [NUnit] ncessitent une rfrence sur l'assembly [nunit.framework.dll] la classe de test [NUnit] rcupre une instance de l'objet tester via Spring. Aussi trouve-t-on dans le dossier [bin], les fichiers de classes de Spring dans [References], une rfrence l'assembly [Spring-Core.dll] du dossier [bin] dans [bin], un fichier de configuration pour Spring la classe de test a besoin de l'assembly [webarticles-dao.dll] de la couche [dao]. Celui-ci a t plac dans le dossier [bin] et sa rfrence ajoute aux rfrences du projet.
Une classe de test [NUnit] ncessite l'accs aux classes de l'espace de noms [NUnit.Framework]. On y trouve donc l'instruction d'import suivante :
Imports NUnit.Framework
L'espace de noms [NUnit.Framework] se trouve dans " l'assembly " [nunit.framework.dll] qu'on doit ajouter aux rfrences du projet :
L'assembly [nunit.framework.dll] doit tre dans la liste propose si l'installation de [Nunit] a t faite. Il suffit de double-cliquer sur l'assembly pour l'ajouter au projet :
web3tier-dotnet-part1, le 09/04/05
11/68
Namespace istia.st.articles.tests <TestFixture()> _ Public Class NunitTestArticlesArrayList ' l'objet tester Private articlesDao As IArticlesDao <SetUp()> _ Public Sub init() ' on rcupre une instance du fabricant d'objets Spring Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileMode.Open)) ' on demande l'instanciation de l'objet articlesdao articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao) End Sub <Test()> _ Public Sub testGetAllArticles() ' vrification visuelle listArticles() End Sub <Test()> _ Public Sub testClearAllArticles() ' on supprime tous les articles articlesDao.clearAllArticles() ' on demande tous les articles Dim articles As IList = articlesDao.getAllArticles ' vrification : il doit y en avoir 0 Assert.AreEqual(0, articles.Count) End Sub <Test()> _ Public Sub testAjouteArticle() ' suppression de tous les articles articlesDao.clearAllArticles() ' vrification : la table des articles doit tre vide Dim articles As IList = articlesDao.getAllArticles Assert.AreEqual(0, articles.Count) ' on ajoute deux articles articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3)) articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4)) ' vrification : il doit y avoir deux articles articles = articlesDao.getAllArticles Assert.AreEqual(2, articles.Count) ' vrification visuelle listArticles() End Sub <Test()> _ Public Sub testSupprimeArticle() ' suppression de tous les articles articlesDao.clearAllArticles() ' vrification : la table des articles doit tre vide Dim articles As IList = articlesDao.getAllArticles Assert.AreEqual(0, articles.Count) ' on ajoute deux articles articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3)) articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4)) ' vrification : il doit y avoir 2 articles articles = articlesDao.getAllArticles Assert.AreEqual(2, articles.Count) ' on supprime l'article 4 articlesDao.supprimeArticle(4) ' vrification : il doit rester 1 article articles = articlesDao.getAllArticles Assert.AreEqual(1, articles.Count) web3tier-dotnet-part1, le 09/04/05
FileStream("spring-config.xml",
12/68
' vrification visuelle listArticles() End Sub <Test()> _ Public Sub testModifieArticle() ' suppression de tous les articles articlesDao.clearAllArticles() ' vrification Dim articles As IList = articlesDao.getAllArticles Assert.AreEqual(0, articles.Count) ' ajout de 2 articles articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3)) articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4)) ' vrification articles = articlesDao.getAllArticles Assert.AreEqual(2, articles.Count) ' recherche de l'article 3 Dim unArticle As Article = articlesDao.getArticleById(3) ' vrification Assert.AreEqual(unArticle.nom, "article3") ' recherche article 4 unArticle = articlesDao.getArticleById(4) ' vrification Assert.AreEqual(unArticle.nom, "article4") ' modification article 4 articlesDao.modifieArticle(New Article(4, "article4", 44, 44, 44)) ' vrification unArticle = articlesDao.getArticleById(4) Assert.AreEqual(unArticle.prix, 44, 0.000001) ' vrification visuelle listArticles() End Sub <Test()> _ Public Sub testGetArticleById() ' suppression des articles articlesDao.clearAllArticles() ' vrification Dim articles As IList = articlesDao.getAllArticles Assert.AreEqual(0, articles.Count) ' ajout de 2 articles articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3)) articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4)) ' vrification articles = articlesDao.getAllArticles Assert.AreEqual(2, articles.Count) ' recherche article 3 Dim unArticle As Article = articlesDao.getArticleById(3) ' vrification Assert.AreEqual(unArticle.nom, "article3") ' recherche article 4 unArticle = articlesDao.getArticleById(4) ' vrification Assert.AreEqual(unArticle.nom, "article4") End Sub ' listing cran Private Sub listArticles() Dim articles As IList = articlesDao.getAllArticles For i As Integer = 0 To articles.Count - 1 Console.WriteLine(CType(articles(i), Article).ToString) Next End Sub <Test()> _ Public Sub testArticleAbsent() ' suppression de tous les articles articlesDao.clearAllArticles() ' recherche article 1 Dim article As article = articlesDao.getArticleById(1) ' vrification Assert.IsNull(article) ' modification d'un article inexistant Dim i As Integer = articlesDao.modifieArticle(New article(1, "1", 1, 1, 1)) ' a du modifier aucune ligne Assert.AreEqual(i, 0) ' suppression article inexistant i = articlesDao.supprimeArticle(1) ' a du supprimer aucune ligne Assert.AreEqual(0, i) End Sub <Test()> _ Public Sub testChangerStockArticle() ' suppression tous les articles articlesDao.clearAllArticles() web3tier-dotnet-part1, le 09/04/05
13/68
' ajout d'un article Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3)) Assert.AreEqual(nbArticles, 1) ' ajout d'un article nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4)) Assert.AreEqual(nbArticles, 1) ' cration de 100 threads Dim taches(99) As Thread For i As Integer = 0 To taches.Length - 1 ' on cre le thread i taches(i) = New Thread(New ThreadStart(AddressOf dcrmente)) ' on fixe le nom du thread taches(i).Name = "tache_" & i ' on lance l'excution du thread i taches(i).Start() Next ' on attend la fin de tous les threads For i As Integer = 0 To taches.Length - 1 taches(i).Join() Next ' vrifications - article 3 doit avoir un stock de 1 Dim unArticle As Article = articlesDao.getArticleById(3) Assert.AreEqual(unArticle.nom, "article3") Assert.AreEqual(1, unArticle.stockactuel) ' on dcrmente le stock de l'article 4 Dim erreur As Boolean = False Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100) ' vrification : son stock n'a pas du changer Assert.AreEqual(0, nbLignes) ' vrification visuelle listArticles() End Sub Public Sub dcrmente() ' thread lanc System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lanc") ' thread dcrmente le stock articlesDao.changerStockArticle(3, -1) ' thread termin System.Console.Out.WriteLine(Thread.CurrentThread.Name + " termin") End Sub End Class End Namespace
Commentaires :
on a voulu crire un programme de test de l'interface [IArticlesDao] qui soit indpendant de la classe d'implmentation de celleci. Aussi avons-nous utilis Spring pour cacher au programme de test le nom de la classe d'implmentation. la mthode d'attribut <Setup()> rcupre auprs de Spring une rfrence sur l'objet [articlesdao] tester. Celui-ci est dfini dans le fichier [spring-config.xml] suivant : <?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN" "http://www.springframework.net/dtd/spring-objects.dtd"> <objects> <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao"/> </objects> Ce fichier indique le nom de la classe d'implmentation [istia.st.articles.dao.ArticlesDaoArrayList] de l'interface [IArticlesDao] et o la trouver [ webarticles-dao.dll]. L'instanciation ne ncessitant pas de paramtres, aucun n'est dfini ici.
la plupart des tests sont simples comprendre. Le lecteur est invit lire les commentaires. La mthode [testChangerStockArticle] ncessite quelques explications. Elle cre 100 threads chargs de dcrmenter le stock d'un article donn.
<Test()> _ Public Sub testChangerStockArticle() ' suppression tous les articles articlesDao.clearAllArticles() ' ajout d'un article Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3)) Assert.AreEqual(nbArticles, 1) ' ajout d'un article nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4)) Assert.AreEqual(nbArticles, 1) ' cration de 100 threads
web3tier-dotnet-part1, le 09/04/05
14/68
Dim taches(99) As Thread For i As Integer = 0 To taches.Length - 1 ' on cre le thread i taches(i) = New Thread(New ThreadStart(AddressOf dcrmente)) ' on fixe le nom du thread taches(i).Name = "tache_" & i ' on lance l'excution du thread i taches(i).Start() Next ' on attend la fin de tous les threads For i As Integer = 0 To taches.Length - 1 taches(i).Join() Next ' vrifications - article 3 doit avoir un stock de 1 Dim unArticle As Article = articlesDao.getArticleById(3) Assert.AreEqual(unArticle.nom, "article3") Assert.AreEqual(1, unArticle.stockactuel) ' on dcrmente le stock de l'article 4 Dim erreur As Boolean = False Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100) ' vrification : son stock n'a pas du changer Assert.AreEqual(0, nbLignes) ' vrification visuelle listArticles() End Sub
Il s'agit ici de tester les accs concurrents la source de donnes. La mthode charge de mettre jour le stock est la suivante :
Public Sub dcrmente() ' thread lanc System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lanc") ' thread dcrmente le stock articlesDao.changerStockArticle(3, -1) ' thread termin System.Console.Out.WriteLine(Thread.CurrentThread.Name + " termin") End Sub
Elle dcrmente d'une unit le stock de l'article n 3. Si on se rfre au code de la mthode [testChangerStockArticle], on voit que : le stock de l'article n 3 est initialis 101 les 100 threads vont dcrmenter ce stock d'une unit chacun on doit donc avoir un stock de 1 la fin de l'excution de tous les threads Par ailleurs, toujours dans cette mthode, on essaie de faire passer le stock de l'article n 4 a une valeur ngative. On doit chouer. Pour tester la couche [dao], nous gnrons la DLL [tests-webarticles-dao.dll] dans le dossier [bin] du projet [tests] :
Puis, l'aide de l'application [Nunit-Gui], nous chargeons cette DLL et excutons les tests :
web3tier-dotnet-part1, le 09/04/05
15/68
Dans la fentre de gauche, on voit la liste des mthodes testes. La couleur du point qui prcde le nom de chaque mthode indique la russite (vert) ou l'chec (rouge) de la mthode. Le lecteur qui visualise ce document sur cran pourra voir que tous les tests ont t russis. Par la suite, nous considrerons que nous avons une couche [dao] oprationnelle.
Commentaires :
le projet [domain] est de type [bibliothque de classes] les classes ont t mises dans une arborescence de racine le dossier [istia]. Elles sont toutes dans l'espace de noms [istia.st.articles.domain]. la DLL de la couche [dao] a t place dans le dossier [bin] du nouveau projet. Par ailleurs, cette DLL a t ajoute comme rfrence au projet. 16/68
web3tier-dotnet-part1, le 09/04/05
Function getAllArticles() As IList Function getArticleById(ByVal idArticle As Integer) As Article acheter(ByVal panier As Panier) ReadOnly Property erreurs() As ArrayList
rend la liste d'objets [Article] de la source de donnes associe rend l'objet [Article] identifi par [idArticle] valide le panier du client en dcrmentant les stocks des articles achets de la quantit achete - peut chouer si le stock est insuffisant rend la liste des erreurs qui se sont produites - vide si pas d'erreurs
17/68
Return _qte * _article.prix End Get End Property ' identit Public Overrides Function ToString() As String Return "[" + _article.ToString + "," + _qte.ToString + "]" End Function End Class End Namespace
Commentaires :
elle a un constructeur permettant d'initialiser les proprits [article, qte] qui dfinissent un achat.
18/68
End Sub Private Function posAchat(ByVal idArticle As Integer) As Integer ' recherche un achat dans la liste des achats ' rend sa position dans la liste ou -1 si pas trouv Dim achatCourant As Achat Dim trouv As Boolean = False Dim i As Integer = 0 While Not trouv AndAlso i < _achats.Count ' achat courant achatCourant = CType(_achats(i), Achat) ' comparaison avec l'article cherch If achatCourant.article.id = idArticle Then Return i End If 'achat suivant i += 1 End While ' pas trouv Return -1 End Function ' function identit Public Overrides Function ToString() As String Return _achats.ToString End Function End Class End Namespace
Commentaires :
la classe [Panier] a les proprits et mthodes suivantes : la liste des achats du client - liste d'objets de type [Achat] ajoute un achat la liste des achats enlve l'achat de l'article idAchat le montant total des achats du panier rend la chane d'identit du panier
ReadOnly Property achats() As ArrayList ajouter(ByVal unAchat As Achat) enlever(ByVal idAchat As Integer) ReadOnly Property totalPanier() As Double Function ToString() As String
la mthode [posAchat] est une mthode utilitaire qui permet d'obtenir la position dans la liste des achats, d'un achat identifi par le n de l'article achet. La liste des achats est gre de telle faon qu'un article achet plusieurs fois n'occupe qu'une position dans la liste. Ainsi un achat peut-il tre identifi par le n de l'article achet. La mthode [posAchat] rend -1 si l'achat recherch n'existe pas. la mthode [ajouter] ajoute un nouvel achat la liste des achats. Cela revient soit ajouter une nouvelle entre la liste des achats si l'article achet n'existait pas dj dans la liste, soit incrmenter la quantit achete s'il existait dj. la mthode [enlever] permet d'enlever un achat identifi par un n de la liste des achats. Si l'achat n'existe pas, la mthode est silencieuse et ne fait rien. le montant des achats [totalPanier] est maintenu au fil des ajouts et retraits d'achats.
19/68
' liste de tous les articles Try Return _articlesDao.getAllArticles Catch ex As Exception _erreurs = New ArrayList _erreurs.Add("Erreur d'accs aux donnes : " + ex.Message) End Try End Function ' obtenir un article identifi par son n Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDomain.getArticleById ' un article particulier Try Return _articlesDao.getArticleById(idArticle) Catch ex As Exception _erreurs = New ArrayList _erreurs.Add("Erreur d'accs aux donnes : " + ex.Message) End Try End Function ' acheter un panier Public Sub acheter(ByVal panier As Panier) Implements IArticlesDomain.acheter ' achat d'un panier - les stocks des articles achets doivent tre dcrments _erreurs = New ArrayList Dim achat As achat Dim achats As ArrayList = panier.achats For i As Integer = achats.Count - 1 To 0 Step -1 ' dcrmenter stock article i achat = CType(achats(i), achat) Try If _articlesDao.changerStockArticle(achat.article.id, -achat.qte) = 0 Then ' on n'a pas pu faire l'opration _erreurs.Add("L'achat " + achat.ToString + " n'a pu se faire - Vrifiez les stocks") Else ' l'opration s'est faite - on enlve l'achat du panier panier.enlever(achat.article.id) End If Catch ex As Exception _erreurs = New ArrayList _erreurs.Add("Erreur d'accs aux donnes : " + ex.Message) End Try Next End Sub End Class End Namespace
Commentaires :
cette classe implmente les quatre mthodes de l'interface [IArticlesDomain]. Elle a deux champs privs : l'objet d'accs aux donnes la liste des erreurs ventuelles. Elle est accessible via la proprit publique [erreurs]
pour construire une instance de la classe, il faut fournir l'objet permettant l'accs aux donnes :
les mthodes [getAllArticles] et [getArticleById] s'appuient sur les mthodes de mme nom de la couche [dao] la mthode [acheter] valide l'achat d'un panier. Cette validation consiste simplement dcrmenter les stocks des articles achets. L'achat d'un article n'est possible que si son stock le permet. Si ce n'est pas le cas, l'achat est refus : il reste dans le panier et une erreur est signale dans la liste [erreurs]. Un achat valid est retir du panier et le stock de l'article correspondant dcrment de la quantit achete.
web3tier-dotnet-part1, le 09/04/05
20/68
Commentaires : le projet [tests] est de type [bibliothque de classes] les tests [NUnit] ncessitent une rfrence sur l'assembly [nunit.framework.dll] la classe de test [NUnit] rcupre une instance de l'objet tester via Spring. Aussi trouve-t-on dans le dossier [bin], les fichiers de classes de Spring dans [References], une rfrence l'assembly [Spring-Core.dll] du dossier [bin] dans [bin], un fichier de configuration pour Spring la classe de test a besoin de l'assembly [webarticles-dao.dll] de la couche [dao] et de l'assembly [webarticles-domain.dll] de la couche [domain]. Ceux-ci ont t placs dans le dossier [bin] et leurs rfrences ajoutes aux rfrences du projet.
Namespace istia.st.articles.tests <TestFixture()> _ Public Class NunitTestArticlesDomain ' l'objet tester web3tier-dotnet-part1, le 09/04/05
21/68
Private articlesDomain As IArticlesDomain Private articlesDao As IArticlesDao <SetUp()> _ Public Sub init() ' on rcupre une instance du fabricant d'objets Spring Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open)) ' on demande l'instanciation de l'objet articles dao articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao) ' puis celui de l'aobjet articlesdomain articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDomain) End Sub <Test()> _ Public Sub getAllArticles() ' vrification visuelle listArticles() End Sub <Test()> _ Public Sub getArticleById() ' suppression des articles articlesDao.clearAllArticles() ' vrification Dim articles As IList = articlesDomain.getAllArticles Assert.AreEqual(0, articles.Count) ' ajout de 2 articles articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3)) articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4)) ' vrification articles = articlesDomain.getAllArticles Assert.AreEqual(2, articles.Count) ' recherche article 3 Dim unArticle As Article = articlesDomain.getArticleById(3) ' vrification Assert.AreEqual(unArticle.nom, "article3") ' recherche article 4 unArticle = articlesDao.getArticleById(4) ' vrification Assert.AreEqual(unArticle.nom, "article4") End Sub <Test()> _ Public Sub acheterPanier() ' suppression des articles articlesDao.clearAllArticles() ' vrification Dim articles As IList = articlesDomain.getAllArticles Assert.AreEqual(0, articles.Count) ' ajout de 2 articles articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3)) articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4)) ' vrification articles = articlesDomain.getAllArticles Assert.AreEqual(2, articles.Count) ' cration d'un panier avec deux achats Dim panier As New panier panier.ajouter(New Achat(New Article(3, "article3", 30, 30, 3), 10)) panier.ajouter(New Achat(New Article(4, "article4", 40, 40, 4), 10)) ' vrifications Assert.AreEqual(700, panier.totalPanier, 0.000001) Assert.AreEqual(2, panier.achats.Count) ' validation panier articlesDomain.acheter(panier) ' vrifications Assert.AreEqual(0, articlesDomain.erreurs.Count) Assert.AreEqual(0, panier.achats.Count) ' recherche article 3 Dim unArticle As Article = articlesDomain.getArticleById(3) ' vrification Assert.AreEqual(unArticle.stockactuel, 20) ' recherche article 4 unArticle = articlesDao.getArticleById(4) ' vrification Assert.AreEqual(unArticle.stockactuel, 30) ' nouveau panier panier.ajouter(New Achat(New Article(3, "article3", 30, 30, 3), 100)) ' validation panier articlesDomain.acheter(panier) ' vrifications Assert.AreEqual(1, articlesDomain.erreurs.Count) ' recherche article 3 unArticle = articlesDomain.getArticleById(3) ' vrification Assert.AreEqual(unArticle.stockactuel, 20) End Sub web3tier-dotnet-part1, le 09/04/05
22/68
<Test()> _ Public Sub testRetirerAchats() ' suppression du contenu de ARTICLES articlesDao.clearAllArticles() ' lit la table ARTICLES Dim articles As IList = articlesDao.getAllArticles() Assert.AreEqual(0, articles.Count) ' insertion Dim article3 As New Article(3, "article3", 30, 30, 3) articlesDao.ajouteArticle(article3) Dim article4 As New Article(4, "article4", 40, 40, 4) articlesDao.ajouteArticle(article4) ' lit la table ARTICLES articles = articlesDomain.getAllArticles() Assert.AreEqual(2, articles.Count) ' cration d'un panier avec deux achats Dim monPanier As New Panier monPanier.ajouter(New Achat(article3, 10)) monPanier.ajouter(New Achat(article4, 10)) ' vrifications Assert.AreEqual(700.0, monPanier.totalPanier, 0.000001) Assert.AreEqual(2, monPanier.achats.Count) ' ajouter un article dj achet monPanier.ajouter(New Achat(article3, 10)) ' vrifications ' le total doit tre pass 1000 Assert.AreEqual(1000.0, monPanier.totalPanier, 0.000001) ' toujours 2 articles dans le panier Assert.AreEqual(2, monPanier.achats.Count) ' qt article 3 a du passer 20 Dim unAchat As Achat = CType(monPanier.achats(0), Achat) Assert.AreEqual(20, unAchat.qte) ' on retire l'article 3 du panier monPanier.enlever(3) ' vrifications ' le total doit tre pass 400 Assert.AreEqual(400.0, monPanier.totalPanier, 0.000001) ' 1 seul article dans le panier Assert.AreEqual(1, monPanier.achats.Count) ' ce doit tre l'article n 4 Assert.AreEqual(4, CType(monPanier.achats(0), Achat).article.id) End Sub ' listing cran Private Sub listArticles() Dim articles As IList = articlesDomain.getAllArticles For i As Integer = 0 To articles.Count - 1 Console.WriteLine(CType(articles(i), Article).ToString) Next End Sub End Class End Namespace
Commentaires :
on a voulu crire un programme de test de l'interface [IArticlesDomain] qui soit indpendant de la classe d'implmentation de celle-ci. Aussi avons-nous utilis Spring pour cacher au programme de test le nom de la classe d'implmentation. la mthode d'attribut <Setup()> rcupre auprs de Spring une rfrence sur les objets [articlesdomain] et [articlesdao] tester. Ceux-ci sont dfinis dans le fichier [spring-config.xml] suivant : <?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN" "http://www.springframework.net/dtd/spring-objects.dtd"> <objects> <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao" /> <object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain"> <constructor-arg index="0"> <ref object="articlesdao" /> </constructor-arg> </object> </objects> Ce fichier indique pour le singleton [articlesdao], le nom de la classe d'implmentation [istia.st.articles.dao.ArticlesDaoArrayList] et o la trouver [ webarticles-dao.dll]. L'instanciation ne ncessitant pas de paramtres, auncun n'est dfini ici.
web3tier-dotnet-part1, le 09/04/05
23/68
pour le singleton [articlesdomain], le nom de la classe d'implmentation [istia.st.articles.domain.AchatsArticles] et o la trouver [ webarticles-domain.dll]. La classe [AchatsArticles] a un constructeur un paramtre : le singleton grant l'accs la couche [dao]. Ici, celui-ci est dfini comme tant le singleton [articlesdao] dfini prcdemment. la classe de test obtient une instance de la classe tester [articlesdomain] ainsi qu'une instance de la classe d'accs aux donnes [articlesdao]. Ce dernier point est litigieux. La classe de test ne devrait thoriquement pas avoir besoin d'avoir accs la couche [dao] qu'elle n'est mme pas cense connatre. Ici, nous sommes passs outre cette " thique " qui, pour tre respecte, nous aurait obliger crer de nouvelles mthodes dans notre interface [IArticlesDomain].
Pour tester la couche [domain], nous gnrons la DLL [tests-webarticles-domain.dll] dans le dossier [bin] du projet [tests] :
Puis, l'aide de l'application [Nunit-Gui], nous chargeons cette DLL et excutons les tests :
Le lecteur qui visualise ce document sur cran pourra voir que tous les tests ont t russis. Par la suite, nous considrerons que nous avons une couche [domain] oprationnelle.
4.5 Conclusion
Rappelons que nous voulons construire l'application web trois couches suivante :
web3tier-dotnet-part1, le 09/04/05
24/68
1
utilisateur
Contrleur 4 3 Vues
2 Modle
Donnes
SPRING
Le modle M de notre application MVC est dsormais crit et test. Il nous est fourni dans deux DLL [webarticles-dao.dll, webarticles-domain.dll]. Nous pouvons passer la dernire couche, la couche [web] qui contient le contrleur C et les vues V. Nous considrerons tout d'abord, une mthode prsente dans le document [http://tahe.developpez.com/dotnet/aspnet/vol1/]
le contrleur C est assur par deux fichiers [global.asax, main.aspx] les vues V sont assures par des pages aspx
5 La couche [web]
L'architecture MVC de l'application web sera la suivante : Client Interface client Contrleurs [global.asax] [main.aspx] Logique applicative Classes mtier Classes d'accs donnes Donnes Source de donnes
[paniervide.aspx] [infos.aspx]
les classes mtier [domain], les classes d'accs aux donnes [dao] et la source de donnes les pages ASPX toutes les requtes des clients HTTP transitent par les deux contrleurs suivants : global.asax : gre les vts lis au lancement initial de l'application main.aspx : traite individuellement la requte de chaque client
5.1 Le modle
Il a t prsent prcdemment. Il est constitu des DLL [webarticles-dao.dll, webarticles-domain.dll].
web3tier-dotnet-part1, le 09/04/05
25/68
liste.aspx Les vues sont rassembles dans le dossier [vues] de l'application infos.aspx panier.aspx paniervide.aspx erreurs.aspx
requte
signification action du contrleur rponses possibles le client veut la liste des - demande la liste des articles la couche - [LISTE] articles mtier - [ERREURS] le client demande des - demande l'article la couche mtier informations sur l'un des articles affichs dans la vue [LISTE] le client achte un article - [INFOS] - [ERREURS]
action=infos
action=achat
- demande l'article la couche mtier et - [INFOS] si erreur de qt l'intgre dans le panier du client - [LISTE] si pas d'erreur
action=retirerachat
le client veut supprimer un - rcupre le panier dans la session et le - [PANIER] achat de son panier modifie - [PANIERVIDE] - [ERREURS] le client veut visualiser son - rcupre le panier dans la session panier - [PANIER] - [PANIERVIDE] - [ERREURS]
action=panier
action=validationpanier
le client a termin ses achats - met jour dans la base les stocks des - [LISTE] et passe la phase paiement articles achets - [ERREURS] - vide le panier du client des articles dont l'achat a t valid
26/68
<appSettings> <add key="urlMain" value="/webarticles/main.aspx"/> <add key="urlInfos" value="vues/infos.aspx"/> <add key="urlErreurs" value="vues/erreurs.aspx"/> <add key="urlListe" value="vues/liste.aspx"/> <add key="urlPanier" value="vues/panier.aspx"/> <add key="urlPanierVide" value="vues/paniervide.aspx"/> </appSettings> </configuration>
Pour avoir accs la couche [mtier], une classe de la couche [web] pourra demander le singleton [articlesDomain]. Spring instanciera alors un objet de type [istia.st.articles.domain.AchatsArticles]. Pour cette instanciation, il a besoin d'un objet de type [articlesDao], c'est dire d'un objet de type [istia.st.articles.dao.ArticlesDaoArrayList]. Spring instanciera alors un tel objet. A la fin de l'opration, la couche [web] qui a demand le singleton [articlesDomain] a toute la chane qui la relie la source de donnes :
utilisateur
Donnes
27/68
La vue [ERREURS] est charge d'afficher une liste d'erreurs que le contrleur [main.aspx] a place dans le contexte de la requte sous le nom [context.Items("erreurs")]. Il y a plusieurs faons d'crire une telle page. Nous ne nous intressons ici qu' la partie affichage des erreurs. Rappelons qu'une page ASPX a une partie prsentation HTML et une partie code .NET qui prpare les donnes que la partie prsentation doit afficher. Ces deux parties peuvent tre dans un mme fichier [aspx] (solution WebMatrx) ou dans deux fichiers : [aspx] pour la prsentation, [aspx.vb] pour le code. Cette dernire solution est celle de Visual Studio. Pour compliquer les choses, la partie prsentation HTML peut comporter, elle aussi, du code .NET, ce qui tend brouiller la sparation [contrleur] et [prsentation] de la vue. Cette solution est gnralement vivement dconseille. Retirer tout code de la partie [prsentation] a ncessit la cration de bibliothques de balises. Celles-ci "cachent" le code sous l'apparence de balises analogues aux balises HTML. Nous prsentons deux solutions possibles pour la page [ERREURS]. Notre premire solution utilise du code .NET dans la partie [prsentation] de la page. La page ASPX rcupre la liste des erreurs prsente dans la requte dans sa partie contrleur [erreurs.aspx.vb] :
Protected erreurs As ArrayList ... Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on rcupre les erreurs erreurs = CType(context.Items("erreurs"), ArrayList) End Sub
La seconde solution utilise la balise <asp:repeater> de la bibliothque de balises <asp:> d'ASP.NET. Si on construit une page ASPX graphiquement, cette balise est disponible sous la forme d'un composant serveur qu'on dpose sur le formulaire de conception. Si on construit le code ASPX la main, on peut parler de bibliothque de balises. Avec la bibliothque de balises <asp:>, le code ASPX de la vue [ERREURS] prcdente devient le suivant :
<asp:Repeater id="rptErreurs" runat="server"> <HeaderTemplate> <h3>Les erreurs suivantes se sont produites : </h3> <ul> </HeaderTemplate> <ItemTemplate> <li> <%# Container.DataItem %> </li> </ItemTemplate> <FooterTemplate> </ul> </FooterTemplate> </asp:Repeater>
La balise
web3tier-dotnet-part1, le 09/04/05
28/68
sert rpter un motif HTML sur les diffrents lments d'une source de donnes. Ses diffrents lments sont les suivants :
HeaderTemplate ItemTemplate FooterTemplate
le motif HTML afficher avant que les lments de la source de donnes ne soient affichs le motif HTML rpter pour chacun des lments de la source de donnes. L'expression [<%# Container.DataItem %>] sert afficher la valeur de l'lment courant de la source de donnes le motif HTML afficher aprs que les lments de la source de donnes ont t affichs
La source de donnes est lie la balise gnralement dans la partie [contrleur] de la page :
Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater .. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on lie les erreurs rptErreurs With rptErreurs .DataSource = context.Items("erreurs") .DataBind() End With End Sub
Cette liaison peut se faire galement au moment de la conception de la page si la source de donnes est dj connue, une base de donnes existante par exemple. Nous utiliserons dans nos vues une autre balise : <asp:datagrid> qui permet d'afficher une source de donnes sous forme d'un tableau.
web3tier-dotnet-part1, le 09/04/05
29/68
Commentaires :
le projet [web] est de type [bibliothque de classes] et non de type [Application web ASP.NET] comme on pourrait logiquement s'y attendre. Le type [Application web ASP.NET] exige la prsence du serveur web IIS sur la machine de dveloppement ou sur une machine distante. Le serveur IIS n'existe pas en standard sur les machines Windows XP Familial. Or beaucoup de PC sont vendus avec cette version. Afin de permettre aux lecteurs disposant de Windows XP de mettre en oeuvre l'application tudie, nous utiliserons le serveur Web Cassini (voir annexe) disponible gratuitement chez Microsoft et nous remplacerons le projet [Application web ASP.NET] par un projet [bibliothque de classes]. Cela entrane quelques inconvnients qui sont expliqus en annexes. les DLL utilises par l'application sont les suivantes : rassemble les classes de la couche d'accs aux donnes rassemble les classes de la couche mtier contient les classes Spring qui nous permettent d'intgrer les couches web, domain et dao classes de logs - utilises par Spring
Ces DLL sont places dans le dossier [bin] et ajoutes aux rfrences du projet.
Le menu est dynamique et fix par le contrleur. Celui-ci met dans la requte transmise la page ASPX, un attribut de cl "actions" ayant pour valeur associe, un tableau d'lments de type Hashtable(). Chaque lment de ce tableau est un dictionnaire destin gnrer une option du menu de l'entte. Chaque dictionnaire a deux cls : - href : l'url associe l'option de menu - lien : le texte du menu On fera de l'entte un contrle utilisateur. Un contrle utilisateur encapsule un morceau de page (prsentation et code associ) dans un composant rutilisable ensuite dans d'autres pages. Ici, nous voulons rutiliser le composant [entete] dans les autres vues de
web3tier-dotnet-part1, le 09/04/05
30/68
l'application. Le code de prsentation sera dans [entete.ascx] et le code de contrle associ dans [entete.ascx.vb]. Le code de prsentation utilisera un composant <asp:repeater> pour afficher le tableau des options du menu : 1
n type nom rle 1 repeater rptMenu afficher les options de menu source de donnes : un tableau de dictionnaires deux cls : href, lien
Le code de prsentation de la page sera le suivant :
1. <%@ Control codebehind="entete.ascx.vb" Language="vb" autoeventwireup="false" inherits="istia.st.articles.web.EnteteWebArticles" %> 2. <table> 3. <tr> 4. <td> 5. <h2>Magasin virtuel</h2></td> 6. <asp:Repeater id="rptMenu" runat="server"> 7. <ItemTemplate> 8. <td> 9. |<a href='<%# Container.DataItem("href") %>'> 10. <%# Container.DataItem("lien") %> 11. </a> 12. </td> 13. </ItemTemplate> 14. </asp:Repeater> 15. </tr> 16. </table> 17. <hr>
Commentaires :
le composant [repeater] est dfini lignes 6-14 chaque lment de la source de donnes associe au rpteur est un dictionnaire deux cls : href - ligne 9 et lien - ligne 10
Commentaires :
le composant de type [EnteteWebArticles] a une proprit publique [actions] en criture seule - ligne 7 cette proprit permet d'associer au composant <asp:repeater> appel [rptMenu] - ligne 10 - le tableau des options calcul par le contrleur de l'application - lignes 11-12.
Les autres vues de l'application utiliseront l'entte dfini par [entete.ascx]. La page [erreurs.aspx] par exemple, incluera l'entte l'aide du code suivant :
1. 2. 3. 4. 5. 6. 7. 8. <%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %> <%@ Page inherits="istia.st.articles.web.ErreursWebarticles" autoeventwireup="false" Language="vb" %> <HTML> <HEAD> <TITLE>webarticles</TITLE> <META http-equiv="Content-Type" content="text/html; charset=windows-1252"> </HEAD> <body>
web3tier-dotnet-part1, le 09/04/05
31/68
9. 10. 11.
Commentaires :
La ligne 1 dclare que la balise <WA:entete> devra tre associe au composant dfini par le fichier [entete.ascx]. Les attributs [TagPrefix] et [TagName] sont libres. Ceci fait, l'insertion du composant dans le code de prsentation de la page se fait avec la ligne 9. A l'excution, cette balise aura pour effet d'inclure dans le code de la page ASPX qui la contient, celui de la page [entete.ascx]. Le code de contrle [erreurs.aspx.vb] prendra soin d'initialiser ce composant. Il peut le faire de la faon suivante :
1. Public Class ErreursWebarticles 2. Inherits System.Web.UI.Page 3. 4. ' composants de la page 5. ... 6. Protected WithEvents entete As New EnteteWebArticles 7. 8. Private Sub Page_Load(ByVal sender As System.Object, ByVal MyBase.Load 9. ... 10. ' on lie les options de menu rptmenu 11. entete.actions = CType(context.Items("options"), Hashtable()) 12.... 13. End Sub 14. End Class
As
System.EventArgs)
Handles
Commentaires :
la ligne 6 cre un objet de type [EnteteWebArticles] qui est le type du composant cr la ligne 11 initialise la proprit [actions] de cet objet
Elle est affiche la suite d'une requte /main?action=liste ou /main?action=validationpanier. Les lments de la requte du contrleur sont les suivants :
actions listarticles message
objet Hashtable() - le tableau des options du menu ArrayList d'objets de type [Article] objet String - message afficher en bas de page
Chaque lien [Infos] du tableau HTML des articles a une url de la forme [?action=infos&id=ID] o ID est le champ id de l'article affich.
web3tier-dotnet-part1, le 09/04/05
32/68
1 3 2 4 5
n
1 2
nom
entete afficher l'entte
rle
label
DataGridArticles afficher les articles en vente 3 - colonne connexe : entte : Nom, champ : nom 4 - colonne connexe : entte : Prix, champ : prix 5 - colonne hypertexte : texte : Infos, champ Url : id, format URL : /webarticles/main.aspx?action=infos&id={0} lblMessage afficher un message
dans Visual Studio, on slectionne le [DataGrid] pour avoir accs sa feuille de proprits :
on utilise le lien [Mise en forme automatique] ci-dessus pour grer la forme du tableau affich et le lien [Gnrateur de proprits] pour grer son contenu
14. <SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle> 15. <AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle> 16. <HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle> 17. <FooterStyle BackColor="Tan"></FooterStyle> 18. <Columns> 19. <asp:BoundColumn DataField="nom" HeaderText="Nom"></asp:BoundColumn> 20. <asp:BoundColumn DataField="prix" HeaderText="Prix" DataFormatString="{0:C}"></asp:BoundColumn> 21. <asp:HyperLinkColumn Text="Infos" DataNavigateUrlField="id" DataNavigateUrlFormatString="/webarticles/main.aspx?action=infos&id={0}"></asp:HyperLinkColumn> 22. </Columns> 23. <PagerStyle HorizontalAlign="Center" ForeColor="DarkSlateBlue" BackColor="PaleGoldenrod"></PagerStyle> 24. </asp:DataGrid></P> 25. <P> 26. <asp:Label id="lblMessage" runat="server" BackColor="#FFC080"></asp:Label></P> 27. </body> 28.</HTML>
Commentaires :
la ligne 9 dfinit l'entte de la page les lignes 12-24 dfinissent les caractristiques du [DataGrid] la ligne 26 dfinit le label [lblMessage]
Handles
Commentaires :
les composants de la page apparaissent lignes 13-15. A noter qu'on a eu besoin de crer un objet [EnteteWebArticles] avec un oprateur [new] alors que cela a t inutile avec les autres composants. Sans cette cration explicite, on avait une erreur d'excution indiquant que l'objet [entete] ne rfrenait rien. Ce point mriterait d'tre creus. Il ne l'a pas t. le tableau des options du menu de l'entte est pris dans le contexte pour initialiser le composant [entete] de la page - ligne 20 la liste des articles est prise dans le contexte - ligne 22 pour initialiser le composant [DataGridArticles] - lignes 24-27 le composant [lblMessage] est initialis avec un message plac dans le contexte - ligne 29
34/68
Elle est affiche la suite d'une requte /main?action=infos&id=ID ou d'une requte /main?action=achat&id=ID lorsque la quantit achete est errone. Les lments de la requte du contrleur sont les suivants :
actions article msg qte
objet Hashtable() - le tableau des options du menu objet de type [Article] - article afficher objet String - message afficher en cas d'erreur sur la quantit objet String - valeur afficher dans le champ de saisie [Qte]
Les champs [msg] et [qte] sont utiliss en cas d'erreur de saisie sur la quantit :
Cette page contient un formulaire qui est post par le bouton [Acheter]. L'url cible du POST est [?action=achat&id=ID] o ID est l'id de l'article achet.
2 3 4 5 6
9 7 8
35/68
web3tier-dotnet-part1, le 09/04/05
n
1 2 3 6
nom
entete
7 8 9
litID afficher le n de l'article DataGridArticle afficher un article 3 - colonne connexe : entte : Nom, champ : nom 4 - colonne connexe : entte : Prix, champ : prix 5 - colonne connexe : entte : Stock actuel, champ : stockActuel 6 - colonne connexe : entte : Stock minimum, champ : stockMinimum poster le formulaire txtQte saisir la quantit achete lblMsgQte message d'erreur ventuel
Commentaires :
l'entte est inclus dans la page - ligne 9 le litral [litId] est dfini ligne 10 le DataGrid [DataGridArticles] est dfini lignes 12-34 le formulaire est dfini lignes 36-46. Il a de type POST. la cible du POST est fourni par une variable [strAction] - ligne 36. Cette variable devra tre dfinie par le contrleur. 36/68
web3tier-dotnet-part1, le 09/04/05
le champ de saisie de la quantit achete est dfinie ligne 41. C'est un composant HTML serveur (runat=server). Ct code, on y a accs via un objet. la ligne 42 dfinit le label [lblMsgQte] qui contiendra un ventuel message d'erreur sur la quantit saisie
Commentaires :
les composants de la page sont dfinis lignes 10-14 la classe dfinit une proprit publique [strAction] qui sert dfinir la cible du POST du formulaire - lignes 17-25 l'article afficher est rcupr dans le contexte de l'application - ligne 30 le tableau des options du menu de l'entte est pris dans le contexte pour initialiser le composant [entete] de la page - ligne 32 lignes 33-39, le composant [DataGridArticle] est lie une source de donnes de type [ArrayList] ne contenant que l'article rcupr ligne 30 les composants [lblMsgQte, txtQte] sont initialiss avec des informations prises dans le contexte - lignes 42-45 la proprit [straction] est galement initialise avec une information prise dans le contexte - ligne 47. Cette variable sert gnrer l'attribut [action] du formulaire HTML prsent dans la page :
37/68
Elle est affiche la suite d'une requte /main?action=panier ou /main?action=retirerachat&id=ID. Les lments de la requte du contrleur sont les suivants :
actions panier
objet Hashtable() - le tableau des options du menu objet de type [Panier] - le panier afficher
Chaque lien [Retirer] du tableau HTML des achats du panier a une url de la forme [?action=retirerachat&id=ID] o ID est le champ [id] de l'article qu'on veut retirer du panier.
7 2 8
n
1 2
nom
entete
label
DataGridAchats afficher la liste des articles 3- colonne connexe - entte : Article, champ : nom achets 4 - colonne connexe - entte : Qt, champ : qte 5 - colonne connexe - entte : Prix, champ : prix 6 - colonne connexe - entte : Total, champ : total, mise en forme {0:C} 7 - colonne hypertexte - Texte : Retirer, p Url : id, format Url : /webarticles/main.aspx?action=retirerachat&id={0} lblTotal afficher le montant payer
38/68
5. <TITLE>webarticles</TITLE> 6. <META http-equiv="Content-Type" content="text/html; charset=windows-1252"> 7. </HEAD> 8. <body> 9. <WA:entete id="entete" runat="server"></WA:entete> 10. <h2>Contenu de votre panier</h2> 11. <P> 12. <asp:DataGrid id="DataGridAchats" runat="server" BorderWidth="1px" GridLines="Vertical" CellPadding="4" 13. BackColor="White" BorderStyle="None" BorderColor="#DEDFDE" ForeColor="Black" AutoGenerateColumns="False"> 14. <SelectedItemStyle Font-Bold="True" ForeColor="White" BackColor="#CE5D5A"></SelectedItemStyle> 15. <AlternatingItemStyle BackColor="White"></AlternatingItemStyle> 16. <ItemStyle BackColor="#F7F7DE"></ItemStyle> 17. <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#6B696B"></HeaderStyle> 18. <FooterStyle BackColor="#CCCC99"></FooterStyle> 19. <Columns> 20. <asp:BoundColumn DataField="nom" HeaderText="Article"></asp:BoundColumn> 21. <asp:BoundColumn DataField="qte" HeaderText="Qté"></asp:BoundColumn> 22. <asp:BoundColumn DataField="prix" HeaderText="Prix"></asp:BoundColumn> 23. <asp:BoundColumn DataField="totalAchat" HeaderText="Total" DataFormatString="{0:C}"></asp:BoundColumn> 24. <asp:HyperLinkColumn Text="Retirer" DataNavigateUrlField="id" DataNavigateUrlFormatString="/webarticles/main.aspx?action=retirerachat&id={0}"></asp:HyperLinkColu mn> 25. </Columns> 26. <PagerStyle HorizontalAlign="Right" ForeColor="Black" BackColor="#F7F7DE" Mode="NumericPages"></PagerStyle> 27. </asp:DataGrid></P> 28. <P>Total de la commande : 29. <asp:Label id="lblTotal" runat="server"></asp:Label> euros</P> 30. </body> 31.</HTML>
Commentaires
la ligne 9 inclut l'entte lignes 12-27, le composant [DataGridAchats] est dfini ligne 29, le composant [lblTotal] est dfini
39/68
41. Me.article = unAchat.article 42. Me.qte = unAchat.qte 43. End Sub 44. 45. ' id : rend l'id de l'article achet 46. Public ReadOnly Property id() As Integer 47. Get 48. Return article.id 49. End Get 50. End Property 51. 52. ' nom : nom de l'article achet 53. Public ReadOnly Property nom() As String 54. Get 55. Return article.nom 56. End Get 57. End Property 58. 59. ' prix de l'article achet 60. Public ReadOnly Property prix() As Double 61. Get 62. Return article.prix 63. End Get 64. End Property 65. 66. End Class 67. End Class 68.End Namespace
Commentaires :
les composants de la page sont dclars lignes 11-13 l'initialisation du composant [entete] est identique celle trouve dans les pages dj tudies - ligne 17 le panier afficher est rcupr dans la session - ligne 19 l'affichage de ce panier l'aide du composant [DataGridAchats] pose des problmes. La difficult vient de l'initialisation du composant. Rappelons les colonnes de celui-ci : colonne [Article] associe au un champ [nom] de la source de donnes colonne [Qt] associe au un champ [qte] de la source de donnes colonne [Prix] associe au un champ [prix] de la source de donnes colonne [Total] associe au un champ [total] de la source de donnes La source de donnes dont nous disposons est le panier et sa liste d'achats. Cette dernire sera la source de donnes du [DataGrid]. Seulement les objets [Achat] qui vont alimenter les lignes du [DataGrid] n'ont pas les proprits [nom, qte, prix, total] attendues par le [DataGrid]. Aussi cre-t-on ici, spcialement pour le [DataGrid] une source de donnes dont les lments ont les caractristiques attendues par le [DataGrid]. Ces lments seront de type [LigneAchat] une classe cre pour l'occasion et drive de la classe [Achat] - lignes 36-66
la classe [LigneAchat) dfinie, la source de donnes de [DataGridAchats] est construite partir du panier trouv dans la session - lignes 20-30 le montant des achats est affich grce la proprit [totalPanier] de la classe [Panier] - ligne 32
Elle est affiche la suite d'une requte /main?action=panier ou /main?action=retirerachat&id=ID. Les lments de la requte du contrleur sont les suivants :
web3tier-dotnet-part1, le 09/04/05
40/68
actions
n
1
nom entete
Commentaires :
Commentaires :
41/68
Elle est affiche la suite de toute requte menant une erreur sauf pour l'action d'achat avec une quantit errone, qui elle, est traite par la vue [INFOS]. Les lments de la requte du contrleur sont les suivants :
actions erreurs
objet Hashtable() - le tableau des options du menu ArrayList d'objets [String] reprsentant les messages d'erreurs afficher
n
1 2
nom
entete rptErreurs
1. <%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %> 2. <%@ Page codebehind="erreurs.aspx.vb" inherits="istia.st.articles.web.ErreursWebarticles" autoeventwireup="false" Language="vb" %> 3. <HTML> 4. <HEAD> 5. <TITLE>webarticles</TITLE> 6. <META http-equiv="Content-Type" content="text/html; charset=windows-1252"> 7. </HEAD> 8. <body> 9. <WA:entete id="entete" runat="server"></WA:entete> 10. <h3>Les erreurs suivantes se sont produites : 11. </h3> 12. <ul> 13. <asp:Repeater id="rptErreurs" runat="server"> 14. <ItemTemplate> 15. <li> 16. <%# Container.DataItem %> 17. </li> 18. </ItemTemplate> 19. </asp:Repeater></ul> 20. </body> 21.</HTML>
Commentaires :
l'entte est dfini ligne 9 le composant [rptErreurs] est dfini lignes 13-19. Son contenu provient d'une source de donnes qui sera de type [ArrayList] d'objets [String].
web3tier-dotnet-part1, le 09/04/05
42/68
System.EventArgs)
Handles
Commentaires :
le composant [entete] est initialis comme l'habitude, lignes 9 et 14 le composant [rptErreurs] est initialis avec la liste d'erreurs de type [ArrayList] trouve dans le contexte - lignes 16-19
Le contrleur [global.asax.vb] dispose d'une procdure [Session_Start] excute chaque fois qu'un nouveau client arrive. Dans cette procdure, on crera un panier vide pour le client. Ce panier sera maintenu au fil des requtes de ce client particulier. Le code pourrait tre le suivant :
1. 2. 3. 4. 5. 6. 7. Imports Imports Imports Imports Imports Imports Imports System System.Web System.Web.SessionState System.Configuration istia.st.articles.domain System.Collections Spring.Context
web3tier-dotnet-part1, le 09/04/05
43/68
8. 9. Namespace istia.st.articles.web 10. 11. Public Class GlobalWebArticles 12. Inherits System.Web.HttpApplication 13. 14. ' init application 15. Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) 16. 17. ' donnes locales 18. Dim parameters() As String = {"urlMain", "urlErreurs", "urlInfos", "urlListe", "urlPanier", "urlPanierVide"} 19. Dim erreurs As New ArrayList 20. 21. ' on rcupre les paramtres d'initialisation de l'application 22. Dim param As String 23. For i As Integer = 0 To parameters.Length - 1 24. ' lecture dans fichier de conf 25. param = ConfigurationSettings.AppSettings(parameters(i)) 26. If param Is Nothing Then 27. ' on note l'erreur 28. erreurs.Add("Paramtre [" + parameters(i) + "] absent dans le fichier [web.config]") 29. Else 30. ' on mmorise le paramtre dans l'application 31. Application.Item(parameters(i)) = param 32. End If 33. Next 34. ' des erreurs ? 35. If erreurs.Count = 0 Then 36. ' on cre un objet IArticlesDomain d'accs la couche mtier 37. Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext) 38. Dim articlesDomain As IArticlesDomain 39. Try 40. articlesDomain = CType(contexte.GetObject("articlesDomain"), IArticlesDomain) 41. ' on mmorise l'objet dans l'application 42. Application.Item("articlesDomain") = articlesDomain 43. Catch ex As Exception 44. ' on mmorise l'erreur 45. erreurs.Add("Erreur lors de la construction de l'objet d'accs la couche mtier [" + ex.ToString + "]") 46. End Try 47. End If 48. ' les erreurs sont places dans l'application 49. Application.Item("erreurs") = erreurs 50. ' c'est fini s'il y a eu des erreurs 51. If erreurs.Count <> 0 Then Return 52. ' on construit un tableau d'options de menu 53. Dim options As New Hashtable 54. ' on rcupre l'url du contrleur 55. Dim urlMain As String = CType(Application.Item("urlMain"), String) 56. Dim uneOption As Hashtable 57. ' liste des articles 58. uneOption = New Hashtable 59. uneOption.Add("href", urlMain + "?action=liste") 60. uneOption.Add("lien", "Liste des articles") 61. options.Add("liste", uneOption) 62. ' panier 63. uneOption = New Hashtable 64. uneOption.Add("href", urlMain + "?action=panier") 65. uneOption.Add("lien", "Voir le panier") 66. options.Add("panier", uneOption) 67. ' validation panier 68. uneOption = New Hashtable 69. uneOption.Add("href", urlMain + "?action=validationpanier") 70. uneOption.Add("lien", "Valider le panier") 71. options.Add("validationpanier", uneOption) 72. ' on met les options du menu dans l'application 73. Application.Item("options") = options 74. Return 75. End Sub 76. 77. ' init session 78. Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) 79. ' on cre un panier pour le client 80. Session.Item("panier") = New Panier 81. End Sub 82. End Class 83.End Namespace
Commentaires :
les paramtres attendus dans [web.config] sont dfinis dans un tableau - ligne 18 ils sont recherchs dans [web.config]. S'ils sont prsents, ils sont mmoriss dans le contexte de l'application, sinon une erreur est enregistre dans la liste des erreurs [erreurs] - lignes 21-33 44/68
web3tier-dotnet-part1, le 09/04/05
s'il n'y a pas d'erreurs, on demande Spring une rfrence du singleton [articlesDomain] qui gre l'accs la couche [domain] de l'application - lignes 35-47. Les erreurs ventuelles sont enregistres dans [erreurs]. les erreurs sont enregistres dans le contexte de l'application - ligne 49 on quitte la procdure s'il y a eu des erreurs - ligne 51 on cre un tableau de trois dictionnaires. Chacun d'eux a deux cls : href et lien. Ce tableau reprsente les trois options de menu possibles - lignes 52-71 ce tableau est mmoris dans le contexte de l'application - ligne 73 chaque nouveau client, la procdure [Session_Start] est excute. On y cre un panier vide dans la session du client - lignes 7881
mthode
demande GET /main?action=liste GET /main?action=infos&id=ID POST /main?action=achat&id=ID - la qt achete fait partie des paramtres posts GET /main?action=retirerachat&id=ID GET /main?action=panier GET /main?action=validationpanier
doInfos
doAchat
doRetirerAchat
doPanier doValidationPanier
traitement - demander la liste des articles la classe mtier - l'afficher - demander l'article d'id=ID la classe mtier - l'afficher - demander l'article d'id=ID la classe mtier - l'inclure dans le panier dans la session client - retirer l'article d'id=ID de la liste des achats du panier de la session client - faire afficher le panier de la session client - dcrmenter dans la base les stocks de tous les articles prsents dans le panier de session du client
rponses possibles [LISTE] ou [ERREURS] [INFOS] ou [ERREURS] [LISTE] ou [INFOS] ou [ERREURS] [PANIER] [PANIER] ou [PANIERVIDE] [LISTE] ou [ERREURS]
45/68
29. 30. ' infos sur un article 31. Public Sub doInfos() 32.... 33. End Sub 34. 35. ' achat d'un article 36. Public Sub doAchat() 37.... 38. End Sub 39. 40. ' suppression d'un achat 41. Public Sub doRetirerAchat() 42.... 43. End Sub 44. 45. ' visualisation panier 46. Public Sub doPanier() 47.... 48. End Sub 49. 50. ' achat panier 51. Public Sub doValidationPanier() 52.... 53. End Sub 54. 55. End Class 56. 57.End Namespace
Commentaires : la classe a deux champs privs qui seront partags entre les mthodes - lignes 15-16 : articlesDomain : le singleton d'accs la couche [domain] options : le tableau des dictionnaires des options de menu la procdure [Page_Load] : initialisera les deux champs privs de la classe rcuprera le paramtre [action] de la requte et fera excuter la mthode de traitement de cette action.
46/68
Commentaires :
chaque chargement de page, on s'assure que l'initialisation de l'application faite par [global.asax] s'est bien droule. pour cela, on rcupre dans le contexte de l'application, la liste des erreurs place l par [global.asax] - ligne 4 si cette liste est non vide, on fait afficher la vue [ERREURS] - lignes 6-10 on rcupre le singleton [articlesDomain] plac par [global.asax] dans le contexte de l'application et on le mmorise dans le champ priv [articlesDomain] afin qu'il soit disponible aux diffrentes mthodes de la classe - ligne 12 on fait un travail analogue avec le tableau des options de menu - ligne 14 on rcupre le paramtre [action] de la requte - ligne 16 on fait excuter la mthode correspondant l'action demande. Une action non prvue est considre comme l'action [liste] lignes 16-36
Commentaires :
on met les erreurs ventuelles dans un [ArrayList] - ligne 4 la liste des articles est demande au singleton [articlesDomain] - lignes 5-12 s'il y a eu des erreurs, on envoie la vue [ERREURS] - lignes 13-19 sinon on envoie la vue [LISTE] - lignes 20-24
web3tier-dotnet-part1, le 09/04/05
47/68
Commentaires : on met les erreurs ventuelles dans un [ArrayList] - ligne 4 l'identifiant de l'article demand est rcupr dans la requte - ligne 6 cet identifiant est vrifi. Il doit tre prsent et ce doit tre un entier. Si ce n'est pas le cas, la vue [ERREURS] est envoye avec le message d'erreur appropri - lignes 7-27 une fois l'identifiant vrifi, l'article est demand au singleton [articlesDomain]. S'il se produit une exception, la vue [ERREURS] est envoye - lignes 29-39 web3tier-dotnet-part1, le 09/04/05 48/68
si l'article n'a pas t trouv, la vue [ERREURS] est envoye - lignes 41-48 si l'article a t trouv, il est plac dans la session de l'utilisateur puis affich dans la vue [INFOS] - lignes 50-56
Commentaires :
l'article mis dans la session est rcupr - ligne 5 s'il n'est pas l (la session a pu expirer), on affiche la vue [LISTE] - lignes 7-10 la quantit achete est rcupre dans la requte - ligne 12 sa validit est vrifie - lignes 13-29 en cas d'invalidit, selon les cas, on envoie la vue [LISTE] - ligne 16 ou la vue [INFOS] - lignes 24-28 si tout est normal, l'achat est enregistr dans le panier - lignes 31-32 puis la vue [LISTE] est envoye - ligne 34
web3tier-dotnet-part1, le 09/04/05
49/68
Hashtable),
Commentaires :
on rcupre le panier dans la session - ligne 4. On ne vrifie pas ici qu'on rcupre bien quelque chose. Il faudrait le faire car la session a pu expirer. si le panier est vide, on envoie la vue [PANIERVIDE] - lignes 6-10 sinon on envoie la vue [PANIER] - lignes 11-14
Commentaires :
on rcupre le panier dans la session - ligne 4. On ne vrifie pas ici qu'on rcupre bien quelque chose. Il faudrait le faire car la session a pu expirer. on rcupre dans la requte l'identifiant [id] de l'article enlever - ligne 8. l'achat correspondant est enlev du panier - ligne 10 50/68
web3tier-dotnet-part1, le 09/04/05
la validit de l'identifiant de l'article achet n'a pas t vrifie ici. Si celui-ci a un type invalide, une exception aura lieu et sera traite lignes 11-13. S'il est valide mais inexistant, la mthode [panier.enlever] - ligne 10 - ne fait rien. on fait afficher le nouveau panier - ligne 16
Commentaires :
on rcupre le panier dans la session - ligne 6. On ne vrifie pas ici qu'on rcupre bien quelque chose. Il faudrait le faire car la session a pu expirer. dans le cas o la session a expir, on aura un pointeur [nothing] pour le panier et la mthode [acheter] - ligne 9 - lancera une exception et la vue [ERREURS] sera envoye. Seulement le message d'erreur sera peu explicite pour l'utilisateur. lignes 8-16, on essaie de valider le panier rcupr dans la session. Certains achats peuvent tre non valids si la quantit demande excde le stock de l'article demand. Ces cas sont mmoriss par la mthode [acheter] dans une liste d'erreurs qui est rcupre ligne 11. si on a des erreurs, la vue [ERREURS] est envoye - lignes 18-23 sinon c'est la vue [LISTE] qui est envoye - lignes 25-26
6 Conclusion
Nous avons ici dvelopp une application selon un modle MVC. Il ne semble pas exister (avril 2005) de "frameworks" professionnels de dveloppement MVC en ASP.NET comme il en existe en Java (Struts, Spring, ...). Le projet [Spring.net] devrait en proposer un rapidement. En attendant cet avnement, la mthode prcdente permet un dveloppement MVC viable pour des applications de taille moyenne.
web3tier-dotnet-part1, le 09/04/05
51/68
7 Annexes
7.1 Construire un projet web avec Visual Studio.net sur XP familial
Visual Studio.net permet de construire diffrents types de projets :
Pour construire un projet d'application web, on choisit normalement le type [Application Web ASP.NET]. Ce type de projet ncessite la prsence d'un serveur web IIS local ou distant. Si on travaille sur une machine Windows XP, ce serveur n'existe pas et il n'est pas possible de l'installer. On ne peut donc crer de projet de type [Application Web ASP.NET]. On peut contourner l'obstacle en acceptant quelques inconvnients mineurs. Il suffit :
d'utiliser le serveur web Cassini en lieu et place du serveur IIS. Il est disponible librement sur le site de Microsoft. d'utiliser un projet [Bibliothque de classes] en lieu et place du projet [Application Web ASP.NET]
supprimer [Class1.vb]
le fichier [demo.aspx] est reconnu comme une page web et un diteur de page lui est associ. Celui-ci a deux panneaux : [design] pour construire graphiquement la page [HTML] pour avoir accs au code HTML de la page
web3tier-dotnet-part1, le 09/04/05
52/68
faire [Affichage/Code] pour faire afficher la partie code VB de la page. Rien ne se passe. On n'a pas accs au code VB de la page. aller dans le panneau [HTML] et inscrire le code qui va relier la page [demo.aspx] au code [demo.aspx.vb] :
demander voir le code de contrle associ la page par [Affichage code - F7]. On obtient le fichier [demo.aspx.vb] :
la page [demo.aspx] est dsormais reconnue comme une page web [.aspx] avec un code [.aspx.vb] associ. revenons sur le panneau [Design] de [demo.aspx] et dessinons la page suivante :
53/68
passons dans le code [demo.aspx.vb] pour y crire le code de contrle qui mettra l'heure dans le composant [lblHeure]. On constate que celui-ci n'est pas reconnu par Intellisense, l'outil d'aide l'criture de code. fermer [demo.aspx] et [demo.aspx.vb] en les sauvegardant auparavant puis les rouvrir. Passer dans le code [demo.aspx.vb]. Cette fois-ci le composant [lblHeure] de [demo.aspx] est bien reconnu par Intellisense dans le code [demo.aspx.vb]. Complter le code :
gnrer le projet par [Gnrer/Gnrer demo]. Si la gnration se passe correctement, la DLL est alors gnre dans le dossier [bin] du projet :
Nous sommes prts pour les tests. Nous configurons le serveur Cassini (cg paragraphe suivant) de la faon suivante :
le chemin de l'excutable le chemin du dossier du projet web Visual Studio le chemin virtuel associ
lancer Cassini. Son icne s'installe dans la barre des tches. Cliquer droit dessus et prendre l'option [Show details] pour vrifier la correcte configuration du serveur web :
web3tier-dotnet-part1, le 09/04/05
54/68
avec un navigateur, demandons la page que nous avons construite, en demandant l'URL [http://localhost/demo/demo.aspx]. Nous obtenons le rsultat suivant :
Nous avons russi crer une application web sous Visual Studio en utilisant : un projet de type [bibliothque de classes] le serveur web Cassini Nous savons maintenant construire des applications web sur des postes ne disposant pas du serveur IIS, comme c'est le cas des postes windows XP, dition familiale.
On suivra attentivement la dmarche d'installation du produit : tlcharger et installer la plate-forme .NET (1.1 en mars 2004) tlcharger et installer WebMatrix tlcharger et installer MSDE (Microsoft Data Engine) qui est une version limite de SQL Server.
Une fois l'installation termine, le produit [WebMatrix] est disponible dans les programmes installs :
web3tier-dotnet-part1, le 09/04/05
55/68
web3tier-dotnet-part1, le 09/04/05
56/68
2 3 4
Lors du dmarrage initial, [WebMatrix] demande les caractristiques du nouveau projet.C'est sa configuration par dfaut. On peut le configurer pour qu'il ne fasse pas apparatre cette bote de dialogue au dmarrage. On l'obtient alors par l'otion [File/New File]. [WebMatrix] permet de construire des squelettes pour diffrentes applications web. Ci-dessus, nous avons prcis avec (1) que nous voulions construire une application [ASP.ET Page] qui est une page Web. Avec (2), nous prcisons le dossier dans lequel sera place cette page Web. Dans (3) nous donnons le nom de la page. Elle doit avoir le suffixe .aspx. Enfin dans (4), nous prcisons que nous voulons travailler avec le langage VB.NET, [WebMatrix] supportant par ailleurs les langages C# et J#. Ceci fait, [WebMatrix] affiche une page d'dition du fichier [demo1.aspx]. Nous y plaons le code suivant :
l'onglet [Design] permet de "dessiner" la page web que l'on veut construire. Cela se passe comme avec un IDE de construction d'applications windows. la conception graphique de la page Web dans [Design] va gnrer du code HTML dans l'onglet [HTML] la page Web peut contenir des contrles gnrant des vnements auxquels il faut ragir, un bouton par exemple. Ces vnements seront grs par du code VB.NET qui sera plac dans l'onglet [Code] au final, le fichier demo1.aspx est un fichier texte mlangeant code HTML et code VB.NET, rsultat de la conception graphique faite dans [Design], du code HTML qu'on a pu ajouter la main dans [HTML] et du code VB.NET plac dans [Code]. La totalit du fichier est disponible dans l'onglet [All]. un dveloppeur ASP.ET expriment peut construire le fichier demo1.aspx directement avec un diteur de texte sans l'aide d'aucun IDE.
57/68
' </script> <html> <head> </head> <body> <form runat="server"> <!-- Insert content here --> </form> </body> </html>
Nous n'allons pas essayer ici d'expliquer ce code. Nous le transformons de la faon suivante :
<html> <head> <title>Dmo asp.net </title> </head> <body> Il est <% =Date.Now.ToString("hh:mm:ss") %> </body> </html>
Le code ci-dessus est un mlange de HTML et de code VB.NET. Celui-ci a t plac dans les balises <% ... %>. Pour excuter ce code, nous utilisons l'option [View/Start]. [WebMatrix] lance alors le serveur Web Cassini s'il n'est pas dj lanc
On peut accepter les valeurs par dfaut proposes dans cette bote de dialogue et choisir l'option [Start]. Le serveur Web est alors actif. [WebMatrix] va alors lancer le navigateur par dfaut de la machine sur laquelle il se trouve et demander l'URL http://localhost:8080/demo1.aspx :
Il est possible d'utiliser le serveur Cassini en-dehors de [WebMatrix]. L'excutable du serveur se trouve dans <WebMatrix>\<version>\WebServer.exe o <WebMatrix> est le rpertoire d'installation de [WebMatrix] et <version> son n de version :
web3tier-dotnet-part1, le 09/04/05
58/68
L'application [WebServer] appele galement serveur web Cassini admet trois paramtres : /port : n de port du service web. Peut-tre quelconque. A par dfaut la valeur 80 /path : chemin physique d'un dossier du disque /vpath : dossier virtuel associ au dossier physique prcdent. On prtera attention au fait que la syntaxe n'est pas /path=chemin mais /vpath:chemin, contrairement ce que dit l'exemple [Example] du panneau d'aide ci-dessus.
web3tier-dotnet-part1, le 09/04/05
59/68
Associons au dossier physique [d:\data\devel\webmatrix] le dossier virtuel [/webmatrix]. Le serveur web pourrait tre lanc de la faons suivante :
E:\Program Files\Microsoft ASP.NET Web Matrix\v0.6.812>webserver /port:100 /path:"d:\data\devel\webmatrix" /vpath:"/webmatrix"
Le serveur Cassini est alors actif et son icne apparat dans la barre des tches. Si on double-clique dessus :
On retrouve les paramtres de lancement du serveur. On dispose galement de la possibilit d'arrter [Stop] ou de relancer [Restart] le serveur web. Si on clique sur le lien [Root URL], on obtient la racine de l'arborescence web du serveur dans un navigateur :
On voit donc que si le dossier physique P=[d:\data\devel\webmatrix] a t associ au dossier virtuel V=[/webmatrix] et que le serveur travaille sur le port 100, la page web [demo1.aspx] se trouvant physiquement dans [P\demos] sera accessible localement via l'URL [http://localhost:100/V/demos/demo1.aspx]. web3tier-dotnet-part1, le 09/04/05 60/68
Afin de ne pas tre oblig de passer par une fentre DOS pour lancer le serveur Cassini, on pourra crer un raccourci vers l'excutable du serveur avec des proprits analogues aux suivantes :
Cible Dmarrer
"C:\Program Files\Microsoft ASP.NET Web Matrix\v0.6.812\WebServer.exe" /port:80 /path:"D:\data\serge\travail\2004-2005\aspnet\webarticles-010405\version3\web" /vpath:"/webarticles" dans "C:\Program Files\Microsoft ASP.NET Web Matrix\v0.6.812"
web3tier-dotnet-part1, le 09/04/05
61/68
Dans ce document, nous n'avons utilis que le contenu du dossier [bin] de la distribution :
Dans un projet Visual Studio utilisant Spring, il faut faire systmatiquement deux choses :
mettre les fichiers ci-dessus dans le dossier [bin] du projet ajouter au projet une rfrence l'assembly [Spring.Core.dll]
Tlchargez cette version et installez la. L'installation cre un dossier o on trouvera la version graphique de test :
web3tier-dotnet-part1, le 09/04/05
62/68
La flche ci-dessus dsigne l'utilitaire graphique de test. L'installation a galement ajout de nouveaux lments au rfrentiel d'assemblages de Visual Studio que nous allons dcouvrir maintenant. Crons le projet Visual Studio suivant :
63/68
Public Sub init() Console.WriteLine("init personne {0}", Me.ToString) End Sub ' mthode close Public Sub close() Console.WriteLine("destroy personne {0}", Me.ToString) End Sub End Class
les mthodes sont dotes d'attributs tels <Setup()>, <TearDown()>, ... pour que ces attributs soient reconnus, il faut que : le projet rfrence l'assembly [nunit.framework.dll] la classe de test importe l'espace de noms [NUnit.Framework]
La rfrence est obtenue en cliquant droit sur [References] dans l'explorateur de solutions :
web3tier-dotnet-part1, le 09/04/05
64/68
L'assembly [nunit.framework.dll] doit tre dans la liste propose si l'installation de [Nunit] s'est bien passe. Il suffit de doublecliquer sur l'assembly pour l'ajouter au projet :
Ceci fait, la classe de test [NunitTestPersonne] doit importer l'espace de noms [NUnit.Framework] :
Imports NUnit.Framework
l'attribut <Test()> dsigne une mthode tester l'attribut <Setup()> dsigne la mthode excuter avant chaque mthode teste l'attribut <TearDown()> dsigne la mthode excuter aprs chaque mthode teste la mthode Assert.AreEqual permet de tester l'galit de deux entits.Il existe de nombreuses autres mthodes de type Assert.xx. l'utilitaire NUnit arrte l'excution d'une mthode teste ds qu'une mthode [Assert] choue et affiche un message d'erreur. Sinon il affiche un message de russite.
La DLL gnre s'appellera [nunit-demos-1.dll] et sera place par dfaut dans le dossier [bin] du projet. Gnrons notre projet. Nous obtenons dans le dossier [bin] :
Lanons maintenant l'utilitaire de test graphique Nunit. Rappelons qu'il se trouve dans <Nunit>\bin et qu'il s'appelle [nunitgui.exe]. <Nunit> dsigne le dossier d'installation de [Nunit]. On obtient l'interface suivante :
web3tier-dotnet-part1, le 09/04/05
65/68
Utilisons l'option de menu [File/Open] pour charger la DLL [nunit-demos-1.dll] de notre projet :
[Nunit] est capable de dtecter automatiquement les classes de test qui se trouvent dans la DLL charge. Ici, il trouve la classe [NunitTestPersonne]. Il affiche alors toutes les mthodes de la classe ayant l'attribut <Test()>. Le bouton [Run] permet de lancer les tests sur l'objet slectionn. Si celui-ci est la classe [NunitTestPersonne], toutes les mthodes affiches sont testes. On peut demander le test d'une mthode particulire en la slectionnant et en demandant son excution par [Run]. Demandons l'excution de la classe :
Un test russi sur une mthode est symbolis par un point vert ct de la mthode dans la fentre de gauche. Un test rat est symbolis par un point rouge. La fentre [Console.Out] droite montre les affichages cran produits par les mthodes testes. Ici, nous avons voulu suivre le droulement d'un test :
1. 2. 3. 4. setup test dbut test fin test teardown test
la ligne 1 montre que la mthode d'attribut <Setup()> est excute avant le test les lignes 2-3 sont produites par la mthode [demo] teste (voir le code plus haut) la ligne 4 montre que la mthode d'attribut <TearDown()> est excute aprs le test
web3tier-dotnet-part1, le 09/04/05
66/68
1INTRODUCTION........................................................................................................................................................................ 2
2L'APPLICATION WEBARTICLES......................................................................................................................................... 2
4LE MODLE............................................................................................................................................................................... 4 4.1LA BASE DE DONNES..................................................................................................................................................................... 4 4.2LES ESPACES DE NOMS DU MODLE.................................................................................................................................................. 4 4.3LA COUCHE [DAO]..........................................................................................................................................................................5 4.3.1STRUCTURE DE LA COUCHE............................................................................................................................................................ 5 4.3.2LA CLASSE [ARTICLE]...................................................................................................................................................................6 4.3.3L'INTERFACE [IARTICLESDAO].......................................................................................................................................................7 4.3.4LA CLASSE D'IMPLMENTATION [ARTICLESDAOARRAYLIST]............................................................................................................... 8 4.3.5GNRATION DE L'ASSEMBLY DE LA COUCHE [DAO]......................................................................................................................... 10 4.3.6TESTS NUNIT DE LA COUCHE [DAO].............................................................................................................................................. 10 4.4LA COUCHE [DOMAIN].................................................................................................................................................................. 16 4.4.1STRUCTURE DE LA COUCHE.......................................................................................................................................................... 16 4.4.2L'INTERFACE [IARTICLESDOMAIN]................................................................................................................................................17 4.4.3LA CLASSE [ACHAT]...................................................................................................................................................................17 4.4.4LA CLASSE [PANIER].................................................................................................................................................................. 18 4.4.5LA CLASSE [ACHATSARTICLES]....................................................................................................................................................19 4.4.6GNRATION DE L'ASSEMBLY DE LA COUCHE [DOMAIN].................................................................................................................... 20 4.4.7TESTS NUNIT DE LA COUCHE [DOMAIN]........................................................................................................................................ 21 4.5CONCLUSION................................................................................................................................................................................24 5LA COUCHE [WEB]................................................................................................................................................................ 25 5.1LE MODLE................................................................................................................................................................................. 25 5.2LES VUES.................................................................................................................................................................................... 25 5.3LES CONTRLEURS....................................................................................................................................................................... 26 5.4CONFIGURATION DE L'APPLICATION............................................................................................................................................... 26 5.4.1LES CHANGEMENTS D'URL............................................................................................................................................................ 26 5.4.2LE CHANGEMENT DES CLASSES D'IMPLMENTATION DES INTERFACES....................................................................................................27 5.4.3LES CHANGEMENTS LIS AU SGBD OU LA BASE DES DONNES.......................................................................................................27 5.5LA BIBLIOTHQUE DE BALISES <ASP:>........................................................................................................................................... 28 5.6STRUCTURE DE LA SOLUTION VISUAL STUDIO DE L'APPLICATION [WEBARTICLES]...............................................................................29 5.7LES VUES ASPX......................................................................................................................................................................... 30 5.7.1LE COMPOSANT UTILISATEUR [ENTETE.ASCX]...................................................................................................................................30 5.7.2LA VUE [LISTE.ASPX].................................................................................................................................................................. 32 5.7.3LA VUE [INFOS.ASPX]..................................................................................................................................................................34 5.7.4LA VUE [PANIER.ASPX]................................................................................................................................................................37 5.7.5LA VUE [PANIERVIDE.ASPX]......................................................................................................................................................... 40 5.7.6LA VUE [ERREURS.ASPX]............................................................................................................................................................. 41 5.8LES CONTRLEURS GLOBAL.ASAX, MAIN.ASPX................................................................................................................................. 43 5.8.1LE CONTRLEUR [GLOBAL.ASAX.VB]............................................................................................................................................. 43 5.8.2LE CONTRLEUR [MAIN.ASPX.VB]..................................................................................................................................................45 5.8.3LA MTHODE [PAGE_LOAD]........................................................................................................................................................46 5.8.4TRAITEMENT DE L'ACTION [LISTE]................................................................................................................................................. 47 5.8.5TRAITEMENT DE L'ACTION [INFOS].................................................................................................................................................47 5.8.6TRAITEMENT DE L'ACTION [ACHAT]............................................................................................................................................... 49 5.8.7TRAITEMENT DE L'ACTION [PANIER]...............................................................................................................................................49 5.8.8TRAITEMENT DE L'ACTION [RETIRERACHAT].................................................................................................................................... 50 5.8.9TRAITEMENT DE L'ACTION [VALIDERPANIER]................................................................................................................................... 51
web3tier-dotnet-part1, le 11/08/2006
67/68
6CONCLUSION.......................................................................................................................................................................... 51
7ANNEXES...................................................................................................................................................................................52 7.1CONSTRUIRE UN PROJET WEB AVEC VISUAL STUDIO.NET SUR XP FAMILIAL...................................................................................... 52 7.2O TROUVER LE SERVEUR WEB CASSINI ?...................................................................................................................................... 55 7.3O TROUVER SPRING ?................................................................................................................................................................ 61 7.4 O TROUVER NUNIT ?.................................................................................................................................................................62
web3tier-dotnet-part1, le 11/08/2006
68/68