Skip to content

1. Deel 1

De PDF van het document is beschikbaar |HIER|.

De voorbeelden uit het document zijn beschikbaar |HIER|.

1.1. Introduction

Doelstellingen van het artikel:

  • een webapplicatie met drie lagen schrijven [interface utilisateur, métier, accès aux données]
  • de applicatie configureren met Spring IOC
  • verschillende versies schrijven door de implementatie van een van de drie lagen te wijzigen.

Gebruikte tools:

  • Visual Studio.net voor de ontwikkeling – zie bijlage, paragraaf 3.1;
  • Cassini-webserver voor de uitvoering – zie bijlage paragraaf 3.2;
  • Nunit voor unit-tests – zie bijlage paragraaf 3.4;
  • Spring voor de integratie en configuratie van de lagen van de webapplicatie – zie bijlage paragraaf 3.3;

Op een schaal van beginner-gemiddeld-gevorderd bevindt dit document zich in het gedeelte [intermédiaire-avancé]. Om het te kunnen begrijpen, zijn diverse voorkennisvereisten nodig. Sommige daarvan kunnen worden verkregen uit documenten die ik heb geschreven. In dat geval vermeld ik ze. Het spreekt voor zich dat dit slechts een suggestie is en dat de lezer zijn eigen favoriete documenten kan gebruiken.

Dit document volgt de rode draad van een document dat voor Java [Drielaagse architecturen en MVC-architecturen met Struts, Spring en Java (2005)] is geschreven. We bouwen in VB.NET de in Java geschreven drielaagse webapplicatie MVC. Het idee dat we hier willen overbrengen, is dat de ontwikkelingsplatforms Java en .NET voldoende op elkaar lijken, zodat de vaardigheden die in een van deze twee domeinen zijn opgedaan, in het andere kunnen worden hergebruikt.

Er lijkt geen algemeen erkende MVC-ontwikkelingsoplossing te bestaan. De volgende oplossing volgt de methode die in het document [Webontwikkeling met ASP.NET 1.1 (2004)] is geïntroduceerd. Hoewel deze methode het voordeel heeft dat ze gebruikmaakt van concepten die gangbaar zijn in de J2EE-ontwikkeling, moet ze echter worden gezien voor wat ze is: c.a.d. een van de vele methoden voor MVC-ontwikkeling. Zodra een ontwikkelingsmethode MVC in ASP.NET algemeen aanvaard is, zal deze moeten worden overgenomen. De .NET-versie van Spring, die momenteel in ontwikkeling is, zou wel eens een eerste oplossing kunnen zijn.

1.2. De webapplicatie „articles“

Hier presenteren we de onderdelen van een vereenvoudigde webapplicatie voor e-commerce. Hiermee kunnen webklanten:

  • een lijst met artikelen uit een database te bekijken
  • bepaalde artikelen in een elektronisch winkelmandje te plaatsen
  • deze te bevestigen. Deze bevestiging heeft als enig effect dat de voorraad van de gekochte artikelen in de database wordt bijgewerkt.

De gebruiker krijgt de volgende weergaven te zien:

- de weergave "LISTE" met een lijst van artikelen die te koop zijn
- de weergave [INFOS], die aanvullende informatie over een product geeft:
  • de weergaven [PANIER] en [PANIERVIDE], die de inhoud van het winkelmandje van de klant weergeven
  • de weergave [ERREURS] die eventuele fouten in de applicatie signaleert

Image

1.3. Algemene architectuur van de applicatie

We willen een applicatie bouwen met de volgende drielaagse structuur:

  • de drie lagen zijn onafhankelijk van elkaar gemaakt door het gebruik van interfaces
  • de integratie van de verschillende lagen wordt gerealiseerd door Spring
  • Elke laag heeft een eigen naamruimte: web (laag UI), domain (bedrijfslaag) en dao (gegevenslaag).

De applicatie zal een MVC-architectuur (Model - View - Controller) volgen. Als we het bovenstaande lagenmodel weergeven, past de MVC-architectuur daar als volgt in:

De verwerking van een verzoek van een klant verloopt volgens de volgende stappen:

  1. de klant doet een verzoek aan de controller. Deze controller is in dit geval een .aspx-pagina die een specifieke rol vervult. Alle verzoeken van klanten komen hier langs. Dit is de toegangspoort tot de applicatie. Dit is de C van MVC.
  1. de controller verwerkt dit verzoek. Hiervoor kan hij de hulp nodig hebben van de businesslaag, het zogenaamde M-model in de structuur MVC.
  2. De controller ontvangt een antwoord van de businesslaag. Het verzoek van de klant is verwerkt. Dit kan verschillende mogelijke antwoorden opleveren. Een klassiek voorbeeld is
    • een foutpagina als het verzoek niet correct kon worden verwerkt
    • een bevestigingspagina in het andere geval
  3. de controller kiest welk antwoord (= weergave) naar de klant moet worden verzonden. Dit is meestal een pagina met dynamische elementen. De controller levert deze aan de weergave.
  4. De weergave wordt naar de klant verzonden. Dit is de V van MVC.

1.4. Het model

Hier bekijken we de M van MVC. Het model bestaat hier uit de volgende elementen:

  1. de businessklassen
  2. de klassen voor gegevenstoegang
  3. de database

1.4.1. De database

De database bevat slechts één tabel met de naam ARTICLES. Deze is gegenereerd met de volgende SQL-opdrachten:

CREATE TABLE ARTICLES (
    ID            INTEGER NOT NULL,
    NOM           VARCHAR(20) NOT NULL,
    PRIX          NUMERIC(15,2) NOT NULL,
    STOCKACTUEL   INTEGER NOT NULL,
    STOCKMINIMUM  INTEGER NOT NULL
);
/* beperkingen */
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_ID check (ID>0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_PRIX check (PRIX>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKACTUEL check (STOCKACTUEL>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKMINIMUM check (STOCKMINIMUM>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_NOM check (NOM<>'');
ALTER TABLE ARTICLES ADD CONSTRAINT UNQ_NOM UNIQUE (NOM);
/* primaire sleutel */
ALTER TABLE ARTICLES ADD CONSTRAINT PK_ARTICLES PRIMARY KEY (ID);
id
primaire sleutel die een artikel op unieke wijze identificeert
nom
naam van het artikel
prix
de prijs
stockactuel
huidige voorraad
stockminimum
de voorraad onder welke een herbevoorradingsorder moet worden geplaatst

1.4.2. De naamruimten van het model

Het model M wordt hier geleverd in de vorm van twee naamruimten:

  • istia.st.articles.dao: bevat de klassen voor toegang tot de gegevens van de laag [dao]
  • istia.st.articles.domain: bevat de bedrijfsspecifieke klassen van de laag [domain]

Elk van deze naamruimten wordt gegenereerd in een eigen „assembly”-bestand:

assembly
contenu
rôle
webarticles-dao
- [IArticlesDao]: de interface voor toegang tot de laag [dao].
Dit is de enige interface die de laag [domain] ziet. Ze ziet geen andere.
- [Article]: klasse die een artikel definieert
- [ArticlesDaoArrayList]: implementatieklasse van
de interface [IArticlesDao] met een klasse [ArrayList]
toegangslaag
- bevindt zich volledig in de
[dao] van de 3-tier-architectuur van
de webapplicatie
webarticles-domain
- [IArticlesDomain]: de interface voor toegang tot de laag [domain]. Dit is de enige interface die de weblaag ziet. De weblaag ziet geen andere interfaces.
- [AchatsArticles]: een klasse die [IArticlesDomain] implementeert
- [Achat]: klasse die de aankoop van een klant vertegenwoordigt
- [Panier]: klasse die alle aankopen van een klant vertegenwoordigt
vertegenwoordigt het model van de aankopen op het
web - bevindt zich volledig in de
[domain]-laag van de architectuur
3-tier-architectuur van de webapplicatie

1.4.3. De laag [dao]

De laag [dao] bevat de volgende elementen:

  • [IArticlesDao]: de interface voor toegang tot de laag [dao]

  • [Article]: klasse die een artikel definieert

  • [ArticlesDaoArrayList]: implementatieklasse van de interface [IArticlesDao] met een klasse [ArrayList]

De structuur van het project [Visual Studio] van de laag [dao] is als volgt:

Image

Opmerkingen:

  • het project [dao] is van het type [bibliothèque de classes]
  • De klassen zijn in een boomstructuur onder de map [istia] geplaatst. Ze bevinden zich allemaal in de naamruimte [istia.st.articles.dao].

1.4.3.1. De klasse [Article]

De klasse die een artikel definieert, is de volgende:

Imports System

Namespace istia.st.articles.dao

    Public Class Article

         ' privévelden
        Private _id As Integer
        Private _nom As String
        Private _prix As Double
        Private _stockactuel As Integer
        Private _stockminimum As Integer

         ' artikel-id
        Public Property id() As Integer
            Get
                Return _id
            End Get
            Set(ByVal Value As Integer)
                If Value <= 0 Then
                    Throw New Exception("Le champ id [" + Value.ToString + "] est invalide")
                End If
                Me._id = Value
            End Set
        End Property

         ' artikelname
        Public Property nom() As String
            Get
                Return _nom
            End Get
            Set(ByVal Value As String)
                If Value Is Nothing OrElse Value.Trim.Equals("") Then
                    Throw New Exception("Le champ nom [" + Value + "] est invalide")
                End If
                Me._nom = Value
            End Set
        End Property

         ' artikelprijs
        Public Property prix() As Double
            Get
                Return _prix
            End Get
            Set(ByVal Value As Double)
                If Value < 0 Then
                    Throw New Exception("Le champ prix [" + Value.ToString + "] est invalide")
                End If
                Me._prix = Value
            End Set
        End Property

         ' huidige voorraad artikel
        Public Property stockactuel() As Integer
            Get
                Return _stockactuel
            End Get
            Set(ByVal Value As Integer)
                If Value < 0 Then
                    Throw New Exception("Le champ stockActuel [" + Value.ToString + "] est invalide")
                End If
                Me._stockactuel = Value
            End Set
        End Property

         ' minimale voorraad artikel
        Public Property stockminimum() As Integer
            Get
                Return _stockminimum
            End Get
            Set(ByVal Value As Integer)
                If Value < 0 Then
                    Throw New Exception("Le champ stockMinimum [" + Value.ToString + "] est invalide")
                End If
                Me._stockminimum = Value
            End Set
        End Property

         ' standaardfabrikant
        Public Sub New()
        End Sub

         ' fabrikant met eigenschappen
        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

         ' methode voor artikelidentificatie
        Public Overrides Function ToString() As String
            Return "[" + id.ToString + "," + nom + "," + prix.ToString + "," + stockactuel.ToString + "," + stockminimum.ToString + "]"
        End Function
    End Class
End Namespace

Deze klasse biedt:

  1. een constructor waarmee de 5 gegevens van een artikel kunnen worden vastgelegd: [id, nom, prix, stockactuel, stockminimum]
  2. openbare eigenschappen waarmee de 5 gegevens kunnen worden gelezen en geschreven.
  3. een controle van de gegevens die in het artikel worden ingevoerd. Bij onjuiste gegevens wordt er een uitzondering gegenereerd.
  4. een methode toString waarmee de waarde van een artikel als tekenreeks kan worden opgehaald. Dit is vaak handig bij het debuggen van een applicatie.

1.4.3.2. De interface [IArticlesDao]

De interface [IArticlesDao] is als volgt gedefinieerd:

Imports System
Imports System.Collections

Namespace istia.st.articles.dao

    Public Interface IArticlesDao
         ' lijst van alle artikelen
        Function getAllArticles() As IList
         ' voegt een artikel toe
        Function ajouteArticle(ByVal unArticle As Article) As Integer
         ' verwijdert een artikel
        Function supprimeArticle(ByVal idArticle As Integer) As Integer
         ' een artikel wijzigen
        Function modifieArticle(ByVal unArticle As Article) As Integer
         ' een artikel zoeken
        Function getArticleById(ByVal idArticle As Integer) As Article
         ' verwijdert alle artikelen
        Sub clearAllArticles()
         ' wijzigt de voorraad van een artikel
        Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
    End Interface
End Namespace

De verschillende methoden van de interface hebben de volgende functies:

getAllArticles
haalt alle artikelen uit de gegevensbron op
clearAllArticles
maakt de gegevensbron leeg
getArticleById
haalt het object [Article] op, geïdentificeerd aan de hand van de primaire sleutel
ajouteArticle
maakt het mogelijk een artikel aan de gegevensbron toe te voegen
modifieArticle
maakt het mogelijk een artikel in de gegevensbron te wijzigen
supprimerArticle
hiermee kun je een artikel uit de gegevensbron verwijderen
changerStockArticle
hiermee kunt u de voorraad van een artikel in de gegevensbron wijzigen

De interface stelt clientprogramma's een aantal methoden ter beschikking die uitsluitend door hun handtekeningen worden gedefinieerd. De interface houdt zich niet bezig met de manier waarop deze methoden daadwerkelijk worden geïmplementeerd. Dit zorgt voor flexibiliteit in een toepassing. Het clientprogramma doet zijn aanroepen op een interface en niet op een specifieke implementatie daarvan.

De keuze voor een specifieke implementatie gebeurt via een Spring-configuratiebestand. Om aan te tonen dat voor het testen van de webapplicatie alleen de interface voor gegevenstoegang van belang is en niet de implementatieklasse ervan, gaan we de gegevensbron eerst implementeren met een eenvoudig object [ArrayList]. Later zullen we een oplossing op basis van SGBD presenteren.

1.4.3.3. De implementatieklasse [ArticlesDaoArrayList]

De implementatieklasse [ArticlesDaoArrayList] is als volgt gedefinieerd:

Imports System
Imports System.Collections

Namespace istia.st.articles.dao

    Public Class ArticlesDaoArrayList
        Implements istia.st.articles.dao.IArticlesDao

        Private articles As New ArrayList
        Private Const nbArticles As Integer = 4

         ' standaardfabrikant
        Public Sub New()
             ' er worden enkele artikelen vervaardigd
            For i As Integer = 1 To nbArticles
                articles.Add(New Article(i, "article" + i.ToString, i * 10, i * 10, i * 10))
            Next
        End Sub

         ' lijst van alle artikelen
        Public Function getAllArticles() As IList Implements IArticlesDao.getAllArticles
             ' de lijst met artikelen wordt weergegeven
            SyncLock Me
                Return articles
            End SyncLock
        End Function

         ' alle artikelen verwijderen
        Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles
             ' de lijst met artikelen leegmaken
            SyncLock Me
                articles.Clear()
            End SyncLock
        End Sub

         ' een artikel ophalen op basis van de sleutel
        Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById
             ' het artikel wordt in de lijst gezocht
            SyncLock Me
                Dim ipos As Integer = posArticle(articles, idArticle)
                If ipos <> -1 Then
                    Return CType(articles(ipos), Article)
                Else
                    Return Nothing
                End If
            End SyncLock
        End Function

         ' een artikel toevoegen aan de lijst met artikelen
        Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle
             ' het artikel wordt aan de artikellijst toegevoegd
            SyncLock Me
                 ' er wordt gecontroleerd of het artikel nog niet bestaat
                Dim ipos As Integer = posArticle(articles, unArticle.id)
                If ipos <> -1 Then
                    Throw New Exception("L'article d'id [" + unArticle.id.ToString + "] existe déjà")
                End If
                 ' het artikel wordt toegevoegd
                articles.Add(unArticle)
                 ' het resultaat wordt weergegeven
                Return 1
            End SyncLock
        End Function

         ' een artikel wijzigen
        Public Function modifieArticle(ByVal articleNouveau As Article) As Integer Implements IArticlesDao.modifieArticle
             ' een artikel wordt gewijzigd
            SyncLock Me
                 ' controleer of het bestaat
                Dim ipos As Integer = posArticle(articles, articleNouveau.id)
                 ' als het niet bestaat
                If ipos = -1 Then Return 0
                 ' het bestaat – we wijzigen het
                articles(ipos) = articleNouveau
                 ' het resultaat wordt weergegeven
                Return 1
            End SyncLock
        End Function

         ' een artikel verwijderen op basis van de sleutel
        Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle
             ' een artikel verwijderen
            SyncLock Me
                 ' controleer of het bestaat
                Dim ipos As Integer = posArticle(articles, idArticle)
                 ' als het niet bestaat
                If ipos = -1 Then Return 0
                 ' het bestaat – het wordt verwijderd
                articles.RemoveAt(ipos)
                 ' het resultaat wordt weergegeven
                Return 1
            End SyncLock
        End Function

         ' de voorraad van een artikel, geïdentificeerd aan de hand van zijn sleutel, wijzigen
        Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle
             ' de voorraad van een artikel wijzigen
            SyncLock Me
                 ' controleren of het bestaat
                Dim ipos As Integer = posArticle(articles, idArticle)
                 ' als het niet bestaat
                If ipos = -1 Then Return 0
                 ' het artikel bestaat – de voorraad wordt aangepast indien mogelijk
                Dim unArticle As Article = CType(articles(ipos), Article)
                 ' de voorraad wordt alleen aangepast als deze voldoende is
                If unArticle.stockactuel + mouvement >= 0 Then
                    unArticle.stockactuel += mouvement
                    Return 1
                Else
                    Return 0
                End If
            End SyncLock
        End Function

         ' zoek een artikel op basis van de sleutel
        Private Function posArticle(ByVal listArticles As ArrayList, ByVal idArticle As Integer) As Integer
             ' geeft de positie van het artikel [idArticle] in de lijst weer of -1 als het niet is gevonden
            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
             ' niet gevonden
            Return -1
        End Function
    End Class

End Namespace

Opmerkingen:

  • de gegevensbron wordt gesimuleerd door het privéveld [articles] van het type [ArrayList]
  • De constructor van de klasse maakt standaard 4 records aan in de gegevensbron.
  • Alle methoden voor gegevenstoegang zijn gesynchroniseerd om problemen met gelijktijdige toegang tot de gegevensbron te voorkomen. Op elk moment heeft slechts één thread toegang tot een bepaalde methode.
  • Met de methode [posArticle] kan de positie [0..N] in de bron [ArrayList] worden bepaald van een artikel dat wordt geïdentificeerd aan de hand van zijn nummer. Als het artikel niet bestaat, retourneert de methode de positie -1. Deze methode wordt herhaaldelijk door de andere methoden gebruikt.
  • Met de methode [ajouteArticle] kan een artikel aan de artikellijst worden toegevoegd. De methode retourneert het aantal toegevoegde artikelen: 1. Als het artikel al bestond, wordt er een uitzondering gegenereerd.
  • Met de methode [modifieArticle] kan een bestaand artikel worden gewijzigd. Deze methode retourneert het aantal gewijzigde artikelen: 1 als het artikel bestond, 0 anders.
  • Met de methode [supprimeArticle] kun je een bestaand artikel verwijderen. Deze methode geeft het aantal verwijderde artikelen terug: 1 als het artikel bestond, 0 anders.
  • De methode [getAllArticles] geeft de lijst met alle artikelen weer
  • Met de methode [getArticleById] kunt u een artikel opvragen dat wordt geïdentificeerd aan de hand van zijn nummer. U krijgt de waarde [nothing] als het artikel niet bestaat.
  • De code stelt geen echte moeilijkheden. We laten het aan de lezer over om deze door te nemen en te begrijpen.

1.4.3.4. Genereren van de assembly van de laag [dao]

Het Visual Studio-project is geconfigureerd om de assembly [webarticles-dao.dll] te genereren. Deze wordt gegenereerd in de map [bin] van het project:

1.4.3.5. NUnit-tests van de laag [dao]

In Java worden de klassen getest met het framework [Junit]. In .NET biedt het Nunit-framework dezelfde mogelijkheden voor unit-tests:

Image

De structuur van het Visual Studio-testproject is als volgt:

Image

Opmerkingen:

  • het project [tests] is van het type [bibliothèque de classes]
  • de tests [NUnit] vereisen een verwijzing naar de assembly [nunit.framework.dll]
  • de testklasse [NUnit] haalt via Spring een instantie van het te testen object op. Daarom bevinden zich
    • in de map [bin] de Spring-klassebestanden
    • in [References] een verwijzing naar de assembly [Spring-Core.dll] uit de map [bin]
    • in [bin] een configuratiebestand voor Spring
  • de testklasse heeft de assembly [webarticles-dao.dll] uit de laag [dao] nodig. Deze is in de map [bin] geplaatst en de verwijzing ernaar is toegevoegd aan de projectverwijzingen.

Een testklasse [NUnit] heeft toegang nodig tot de klassen van de naamruimte [NUnit.Framework]. Daarin staat dus de volgende importinstructie:

Imports NUnit.Framework

De naamruimte [NUnit.Framework] bevindt zich in de „assembly“ [nunit.framework.dll], die aan de projectreferenties moet worden toegevoegd:

De assembly [nunit.framework.dll] moet in de voorgestelde lijst staan als [Nunit] is geïnstalleerd. Dubbelklik op de assembly om deze aan het project toe te voegen:

Image

De testklasse [NUnit] van de laag [dao] zou er als volgt uit kunnen zien:

Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports System.Threading
Imports Spring.Objects.Factory.Xml
Imports System.IO

Namespace istia.st.articles.tests

    <TestFixture()> _
    Public Class NunitTestArticlesArrayList
         ' het te testen object
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
             ' er wordt een instantie opgehaald van de Spring-objectgenerator
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open))
             ' we vragen om het instantiëren van het object articlesdao
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
             ' visuele controle
            listArticles()
        End Sub

        <Test()> _
        Public Sub testClearAllArticles()
             ' alle artikelen worden verwijderd
            articlesDao.clearAllArticles()
             ' alle artikelen opvragen
            Dim articles As IList = articlesDao.getAllArticles
             ' controle: er moeten er 0 zijn
            Assert.AreEqual(0, articles.Count)
        End Sub

        <Test()> _
        Public Sub testAjouteArticle()
             ' alle artikelen verwijderen
            articlesDao.clearAllArticles()
             ' controle: de artikeltabel moet leeg zijn
            Dim articles As IList = articlesDao.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' er worden twee artikelen toegevoegd
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             ' controle: er moeten twee artikelen zijn
            articles = articlesDao.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' visuele controle
            listArticles()
        End Sub

        <Test()> _
        Public Sub testSupprimeArticle()
             ' alle artikelen verwijderen
            articlesDao.clearAllArticles()
             ' controle: de artikeltabel moet leeg zijn
            Dim articles As IList = articlesDao.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' er worden twee artikelen toegevoegd
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             ' controle: er moeten 2 artikelen zijn
            articles = articlesDao.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' artikel 4 wordt verwijderd
            articlesDao.supprimeArticle(4)
             ' controle: er moet 1 artikel overblijven
            articles = articlesDao.getAllArticles
            Assert.AreEqual(1, articles.Count)
             ' visuele controle
            listArticles()
        End Sub

        <Test()> _
        Public Sub testModifieArticle()
             ' alle artikelen verwijderen
            articlesDao.clearAllArticles()
             ' controle
            Dim articles As IList = articlesDao.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' 2 artikelen toevoegen
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             ' controle
            articles = articlesDao.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' artikel 3 zoeken
            Dim unArticle As Article = articlesDao.getArticleById(3)
             ' controle
            Assert.AreEqual(unArticle.nom, "article3")
             ' artikel 4 zoeken
            unArticle = articlesDao.getArticleById(4)
             ' controle
            Assert.AreEqual(unArticle.nom, "article4")
             ' wijziging artikel 4
            articlesDao.modifieArticle(New Article(4, "article4", 44, 44, 44))
             ' controle
            unArticle = articlesDao.getArticleById(4)
            Assert.AreEqual(unArticle.prix, 44, 0.000001)
             ' visuele controle
            listArticles()
        End Sub

        <Test()> _
        Public Sub testGetArticleById()
             ' artikelen verwijderen
            articlesDao.clearAllArticles()
             ' controle
            Dim articles As IList = articlesDao.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' toevoeging van 2 artikelen
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             ' controle
            articles = articlesDao.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' artikel 3 zoeken
            Dim unArticle As Article = articlesDao.getArticleById(3)
             ' controle
            Assert.AreEqual(unArticle.nom, "article3")
             ' artikel 4 zoeken
            unArticle = articlesDao.getArticleById(4)
             ' controle
            Assert.AreEqual(unArticle.nom, "article4")
        End Sub

         ' schermweergave
        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()
             ' alle artikelen verwijderen
            articlesDao.clearAllArticles()
             ' artikel zoeken 1
            Dim article As article = articlesDao.getArticleById(1)
             ' controle
            Assert.IsNull(article)
             ' wijziging van een niet-bestaand artikel
            Dim i As Integer = articlesDao.modifieArticle(New article(1, "1", 1, 1, 1))
             ' heeft geen regels gewijzigd
            Assert.AreEqual(i, 0)
             ' verwijdering van een niet-bestaand artikel
            i = articlesDao.supprimeArticle(1)
             ' moest geen regel verwijderen
            Assert.AreEqual(0, i)
        End Sub

        <Test()> _
        Public Sub testChangerStockArticle()
             ' alle artikelen verwijderen
            articlesDao.clearAllArticles()
             ' toevoeging van een artikel
            Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3))
            Assert.AreEqual(nbArticles, 1)
             ' een artikel toegevoegd
            nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
            Assert.AreEqual(nbArticles, 1)
             ' 100 threads aangemaakt
            Dim taches(99) As Thread
            For i As Integer = 0 To taches.Length - 1
                 ' de thread i wordt aangemaakt
                taches(i) = New Thread(New ThreadStart(AddressOf décrémente))
                 ' de naam van de thread wordt vastgelegd
                taches(i).Name = "tache_" & i
                 ' de thread i wordt gestart
                taches(i).Start()
            Next
             ' wachten tot alle threads zijn voltooid
            For i As Integer = 0 To taches.Length - 1
                taches(i).Join()
            Next
             ' controles – artikel 3 moet een voorraad van 1 hebben
            Dim unArticle As Article = articlesDao.getArticleById(3)
            Assert.AreEqual(unArticle.nom, "article3")
            Assert.AreEqual(1, unArticle.stockactuel)
             ' de voorraad van artikel 4 wordt verlaagd
            Dim erreur As Boolean = False
            Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100)
             ' controle: de voorraad mag niet zijn veranderd
            Assert.AreEqual(0, nbLignes)
             ' visuele controle
            listArticles()
        End Sub

        Public Sub décrémente()
             ' thread gestart
            System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lancé")
             ' thread verlaagt de voorraad
            articlesDao.changerStockArticle(3, -1)
             ' thread voltooid
            System.Console.Out.WriteLine(Thread.CurrentThread.Name + " terminé")
        End Sub

    End Class
End Namespace

Opmerkingen:

  • we wilden een testprogramma schrijven voor de interface [IArticlesDao] dat onafhankelijk is van de implementatieklasse ervan. Daarom hebben we Spring gebruikt om de naam van de implementatieklasse voor het testprogramma te verbergen.
  • De attribuutmethode <Setup()> haalt bij Spring een verwijzing op naar het te testen object [articlesdao]. Dit object is gedefinieerd in het volgende bestand [spring-config.xml]:
<?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>

Dit bestand geeft de naam aan van de implementatieklasse [istia.st.articles.dao.ArticlesDaoArrayList] van de interface [IArticlesDao] en waar deze te vinden is: [ webarticles-dao.dll]. Aangezien het instantiëren geen parameters vereist, zijn hier geen parameters gedefinieerd.

  • De meeste tests zijn eenvoudig te begrijpen. De lezer wordt verzocht de commentaren te lezen.
  • De methode [testChangerStockArticle] behoeft enige uitleg. Deze methode maakt 100 threads aan die de voorraad van een bepaald artikel moeten verlagen.
        <Test()> _
        Public Sub testChangerStockArticle()
             ' alle artikelen verwijderen
            articlesDao.clearAllArticles()
             ' artikel toegevoegd
            Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3))
            Assert.AreEqual(nbArticles, 1)
             ' artikel toevoegen
            nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
            Assert.AreEqual(nbArticles, 1)
             ' 100 threads aanmaken
            Dim taches(99) As Thread
            For i As Integer = 0 To taches.Length - 1
                 ' de thread i wordt aangemaakt
                taches(i) = New Thread(New ThreadStart(AddressOf décrémente))
                 ' de naam van de thread wordt vastgelegd
                taches(i).Name = "tache_" & i
                 ' de thread i wordt gestart
                taches(i).Start()
            Next
             ' wachten tot alle threads zijn voltooid
            For i As Integer = 0 To taches.Length - 1
                taches(i).Join()
            Next
             ' controles – artikel 3 moet een voorraad van 1 hebben
            Dim unArticle As Article = articlesDao.getArticleById(3)
            Assert.AreEqual(unArticle.nom, "article3")
            Assert.AreEqual(1, unArticle.stockactuel)
             ' de voorraad van artikel 4 wordt verlaagd
            Dim erreur As Boolean = False
            Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100)
             ' controle: de voorraad mag niet zijn veranderd
            Assert.AreEqual(0, nbLignes)
             ' visuele controle
            listArticles()
        End Sub

Hier gaat het om het testen van gelijktijdige toegang tot de gegevensbron. De methode die de voorraad bijwerkt, is als volgt:

        Public Sub décrémente()
             ' thread gestart
            System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lancé")
             ' thread verlaagt de voorraad
            articlesDao.changerStockArticle(3, -1)
             ' thread voltooid
            System.Console.Out.WriteLine(Thread.CurrentThread.Name + " terminé")
        End Sub

De voorraad van artikel nr. 3 wordt met één eenheid verminderd. Als we kijken naar de code van de methode [testChangerStockArticle], zien we dat:

  • de voorraad van artikel nr. 3 wordt ingesteld op 101
  • de 100 threads zullen deze voorraad elk met één eenheid verminderen
  • dus moet de voorraad aan het einde van de uitvoering van alle threads 1 bedragen

Bovendien wordt, nog steeds in deze methode, geprobeerd de voorraad van artikel nr. 4 op een negatieve waarde te zetten. Dit moet mislukken.

Om de laag [dao] te testen, genereren we de DLL [tests-webarticles-dao.dll] in de map [bin] van het project [tests]:

Vervolgens laden we met behulp van de applicatie [Nunit-Gui] dit bestand DLL en voeren we de tests uit:

Image

In het linkervenster zien we de lijst met geteste methoden. De kleur van het puntje voor de naam van elke methode geeft aan of de methode geslaagd (groen) of mislukt (rood) is. De lezer die dit document op het scherm bekijkt, zal zien dat alle tests zijn geslaagd. Vervolgens gaan we ervan uit dat we een operationele laag [dao] hebben.

1.4.4. De laag [domain]

De laag [domain] bevat de volgende elementen:

  • [IArticlesDomain]: de interface voor toegang tot de laag [domain]

  • [Achat]: klasse die een aankoop definieert

  • [Panier]: klasse die een winkelmandje definieert

  • [AchatsArticles]: implementatieklasse van de interface [IArticlesDomain]

De structuur van de oplossing [Visual Studio] van de laag [domain] is als volgt:

Image

Opmerkingen:

  • het project [domain] is van het type [bibliothèque de classes]
  • de klassen zijn in een boomstructuur onder de hoofdmap [istia] geplaatst. Ze bevinden zich allemaal in de naamruimte [istia.st.articles.domain].
  • de DLL van de laag [dao] is in de map [bin] van het nieuwe project geplaatst. Daarnaast is deze DLL als referentie aan het project toegevoegd.

1.4.4.1. De interface [IArticlesDomain]

De interface [IArticlesDomain] ontkoppelt de laag [métier] van de laag [web]. Deze laatste krijgt via deze interface toegang tot de laag [métier/domain], zonder zich zorgen te maken over de klasse die deze daadwerkelijk implementeert. De interface definieert de volgende acties voor toegang tot de bedrijfslaag:

Imports Article = istia.st.articles.dao.Article

Namespace istia.st.articles.domain
    Public Interface IArticlesDomain
         ' methoden
        Sub acheter(ByVal panier As Panier)
        Function getAllArticles() As IList
        Function getArticleById(ByVal idArticle As Integer) As Article
        ReadOnly Property erreurs() As ArrayList
    End Interface
End Namespace
Function getAllArticles() As IList
geeft de objectenlijst [Article] van de bijbehorende gegevensbron weer
Function getArticleById(ByVal idArticle As Integer) As Article
geeft het object [Article] weer, geïdentificeerd door [idArticle]
acheter(ByVal panier As Panier)
valideert het winkelmandje van de klant door de voorraad van de gekochte artikelen met de gekochte hoeveelheid te verminderen – kan mislukken als de voorraad onvoldoende is
ReadOnly Property erreurs() As ArrayList
geeft de lijst met opgetreden fouten weer – leeg als er geen fouten zijn

1.4.4.2. De klasse [Achat]

De klasse [Achat] vertegenwoordigt een aankoop van de klant:

Imports istia.st.articles.dao

Namespace istia.st.articles.domain

    Public Class Achat

         ' privévelden
        Private _article As article
        Private _qte As Integer

         ' standaardconstructor
        Public Sub New()
        End Sub

         ' constructor met parameters
        Public Sub New(ByVal unArticle As article, ByVal qte As Integer)
             ' via eigenschappen
            Me.article = unArticle
            Me.qte = qte
        End Sub

         ' gekocht artikel
        Public Property article() As article
            Get
                Return _article
            End Get
            Set(ByVal Value As article)
                _article = Value
            End Set
        End Property

         ' aangekochte hoeveelheid
        Public Property qte() As Integer
            Get
                Return _qte
            End Get
            Set(ByVal Value As Integer)
                If Value < 0 Then
                    Throw New Exception("Quantité [" + Value.ToString + "] invalide")
                End If
                _qte = Value
            End Set
        End Property

         ' totaal aankoop
        Public ReadOnly Property totalAchat() As Double
            Get
                Return _qte * _article.prix
            End Get
        End Property

         ' identiteit
        Public Overrides Function ToString() As String
            Return "[" + _article.ToString + "," + _qte.ToString + "]"
        End Function
    End Class

End Namespace

Opmerkingen:

  • de klasse [Achat] heeft de volgende eigenschappen en methoden:
Public Property article() As article
het gekochte artikel
Public Property qte() As Integer
de gekochte hoeveelheid
Public ReadOnly Property totalAchat() As Double
het aankoopbedrag
Public Overrides Function ToString() As String
identiteitsstring van het object
  • het heeft een constructor waarmee de eigenschappen [article, qte] kunnen worden geïnitialiseerd die een aankoop definiëren.

1.4.4.3. De klasse [Panier]

De klasse [Panier] vertegenwoordigt alle aankopen van de klant:

Namespace istia.st.articles.domain

Public Class Panier

     ' privévelden
    Private _achats As New ArrayList
        Private _totalPanier As Double = 0

     ' standaardfabrikant
    Public Sub New()
    End Sub

     ' aankooplijst
    Public ReadOnly Property achats() As ArrayList
        Get
            Return _achats
        End Get
    End Property

     ' totaal van de aankopen
    Public ReadOnly Property totalPanier() As Double
        Get
            Return _totalPanier
        End Get
    End Property

     ' methoden
    Public Sub ajouter(ByVal unAchat As Achat)
         ' er wordt gecontroleerd of de aankoop al bestaat
        Dim iAchat As Integer = posAchat(unAchat.article.id)
        If iAchat <> -1 Then
             ' gevonden
                Dim achatCourant As Achat = CType(_achats(iAchat), Achat)
            achatCourant.qte += unAchat.qte
        Else
             ' niet gevonden
            _achats.Add(unAchat)
        End If
         ' het totaal van het winkelmandje wordt verhoogd
        _totalPanier += unAchat.totalAchat
    End Sub

     ' een aankoop verwijderen
    Public Sub enlever(ByVal idAchat As Integer)
         ' het artikel wordt gezocht
            Dim iachat As Integer = posAchat(idAchat)
         ' als het is gevonden, wordt het verwijderd
        If iachat <> -1 Then
                Dim achatCourant As Achat = CType(_achats(iachat), Achat)
             ' uit het winkelmandje verwijderen
            _achats.RemoveAt(iachat)
             ' het totaal van het winkelmandje verlagen
            _totalPanier -= achatCourant.totalAchat
        End If
    End Sub

    Private Function posAchat(ByVal idArticle As Integer) As Integer
         ' zoek een aankoop in de aankooplijst
         ' geeft de positie in de lijst terug of -1 als het niet gevonden is
        Dim achatCourant As Achat
        Dim trouvé As Boolean = False
        Dim i As Integer = 0
            While Not trouvé AndAlso i < _achats.Count
                 ' huidige aankoop
                achatCourant = CType(_achats(i), Achat)
                 ' vergelijking met het gezochte artikel
                If achatCourant.article.id = idArticle Then
                    Return i
                End If
                 'volgende aankoop
                i += 1
            End While
             ' niet gevonden
            Return -1
    End Function

     ' identiteitsfunctie
    Public Overrides Function ToString() As String
        Return _achats.ToString
    End Function
End Class

End Namespace

Opmerkingen:

  • de klasse [Panier] heeft de volgende eigenschappen en methoden:
ReadOnly Property achats() As ArrayList
de aankooplijst van de klant – lijst met objecten van het type [Achat]
ajouter(ByVal unAchat As Achat)
voegt een aankoop toe aan de aankooplijst
enlever(ByVal idAchat As Integer)
verwijdert de aankoop van het artikel idAchat
ReadOnly Property totalPanier() As Double
het totaalbedrag van de aankopen in het winkelmandje
Function ToString() As String
geeft de identiteitsstring van het winkelmandje weer
  • de methode [posAchat] is een hulpprogramma waarmee de positie in de aankooplijst kan worden verkregen van een aankoop die wordt geïdentificeerd door het artikelnummer van het gekochte artikel. De aankooplijst wordt zo beheerd dat een artikel dat meerdere keren is gekocht slechts één positie in de lijst inneemt. Zo kan een aankoop worden geïdentificeerd aan de hand van het artikelnummer. De methode [posAchat] retourneert -1 als de gezochte aankoop niet bestaat.
  • De methode [ajouter] voegt een nieuwe aankoop toe aan de aankooplijst. Dit komt neer op het toevoegen van een nieuw item aan de aankooplijst als het gekochte artikel nog niet in de lijst voorkwam, of op het verhogen van de gekochte hoeveelheid als het artikel al in de lijst stond.
  • Met de methode [enlever] kan een aankoop, geïdentificeerd door een nummer, uit de aankooplijst worden verwijderd. Als de aankoop niet bestaat, reageert de methode niet en doet ze niets.
  • Het aankoopbedrag van [totalPanier] blijft behouden bij het toevoegen en verwijderen van aankopen.

1.4.4.4. De klasse [AchatsArticles]

De interface [IArticlesDomain] wordt geïmplementeerd door de volgende klasse [AchatsArticles]:

Imports istia.st.articles.dao

Namespace istia.st.articles.domain
    Public Class AchatsArticles
        Implements IArticlesDomain

         'privévelden
        Private _articlesDao As IArticlesDao
        Private _erreurs As ArrayList

         ' constructor
        Public Sub New(ByVal articlesDao As IArticlesDao)
            _articlesDao = articlesDao
        End Sub

         ' foutenlijst
        Public ReadOnly Property erreurs() As ArrayList Implements IArticlesDomain.erreurs
            Get
                Return _erreurs
            End Get
        End Property

         ' artikellijst
        Public Function getAllArticles() As IList Implements IArticlesDomain.getAllArticles
             ' lijst van alle artikelen
            Try
                Return _articlesDao.getAllArticles
            Catch ex As Exception
                _erreurs = New ArrayList
                _erreurs.Add("Erreur d'accès aux données : " + ex.Message)
            End Try
        End Function

         ' een artikel opzoeken aan de hand van het nummer
        Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDomain.getArticleById
             ' een specifiek artikel
            Try
                Return _articlesDao.getArticleById(idArticle)
            Catch ex As Exception
                _erreurs = New ArrayList
                _erreurs.Add("Erreur d'accès aux données : " + ex.Message)
            End Try
        End Function


         ' een winkelmandje kopen
        Public Sub acheter(ByVal panier As Panier) Implements IArticlesDomain.acheter
             ' een winkelmandje kopen – de voorraad van de gekochte artikelen moet worden verminderd
            _erreurs = New ArrayList
            Dim achat As achat
            Dim achats As ArrayList = panier.achats
            For i As Integer = achats.Count - 1 To 0 Step -1
                 ' voorraad van artikel i verlagen
                achat = CType(achats(i), achat)
                Try
                    If _articlesDao.changerStockArticle(achat.article.id, -achat.qte) = 0 Then
                         ' de bewerking kon niet worden uitgevoerd
                        _erreurs.Add("L'achat " + achat.ToString + " n'a pu se faire - Vérifiez les stocks")
                    Else
                         ' de bewerking is uitgevoerd – de aankoop wordt uit het winkelmandje verwijderd
                        panier.enlever(achat.article.id)
                    End If
                Catch ex As Exception
                    _erreurs = New ArrayList
                    _erreurs.Add("Erreur d'accès aux données : " + ex.Message)
                End Try
            Next
        End Sub
    End Class

End Namespace

Opmerkingen:

  • deze klasse implementeert de vier methoden van de interface [IArticlesDomain]. Ze heeft twee privévelden:
_articlesDao As IArticlesDao
het object voor gegevenstoegang
_erreurs As ArrayList
de lijst met eventuele fouten. Deze is toegankelijk via de openbare eigenschap [erreurs]
  • om een instantie van de klasse aan te maken, moet het object worden opgegeven dat toegang tot de gegevens biedt:
Sub New(ByVal articlesDao As IArticlesDao)
  • De methoden [getAllArticles] en [getArticleById] zijn gebaseerd op de gelijknamige methoden van de laag [dao]
  • De methode [acheter] valideert de aankoop van een winkelmandje. Deze validatie bestaat simpelweg uit het verlagen van de voorraad van de gekochte artikelen. Een artikel kan alleen worden gekocht als de voorraad dit toelaat. Als dat niet het geval is, wordt de aankoop geweigerd: het artikel blijft in het winkelmandje en er wordt een fout gemeld in de lijst [erreurs]. Een gevalideerde aankoop wordt uit het winkelmandje verwijderd en de voorraad van het betreffende artikel wordt met de gekochte hoeveelheid verminderd.

1.4.4.5. Genereren van de assembly van de laag [domain]

Het Visual Studio-project is geconfigureerd om de assembly [webarticles-domain.dll] te genereren. Deze wordt gegenereerd in de map [bin] van het project:

1.4.4.6. Tests NUnit van de laag [domain]

De structuur van het Visual Studio-testproject is als volgt:

Image

Opmerkingen:

  • het project [tests] is van het type [bibliothèque de classes]
  • de tests [NUnit] vereisen een verwijzing naar de assembly [nunit.framework.dll]
  • de testklasse [NUnit] haalt via Spring een instantie van het te testen object op. Daarom vinden we
  • in de map [bin] de Spring-klassebestanden
  • in [References] een verwijzing naar de assembly [Spring-Core.dll] uit de map [bin]
  • in [bin] een configuratiebestand voor Spring
  • de testklasse heeft de assembly [webarticles-dao.dll] uit de laag [dao] en de assembly [webarticles-domain.dll] uit de laag [domain] nodig. Deze zijn in de map [bin] geplaatst en hun verwijzingen zijn toegevoegd aan de projectverwijzingen.

Een testklasse NUnit van de laag [domain] zou er als volgt uit kunnen zien:

Imports NUnit.Framework
Imports istia.st.articles.dao
Imports istia.st.articles.domain
Imports Spring.Objects.Factory.Xml
Imports System.IO

Namespace istia.st.articles.tests

    <TestFixture()> _
    Public Class NunitTestArticlesDomain

         ' het te testen object
        Private articlesDomain As IArticlesDomain
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
             ' we halen een instantie op van de Spring-objectgenerator
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open))
             ' we vragen om het instantiëren van het DAO-object ‘articles’
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
             ' en vervolgens die van het 'articlesdomain'-object
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDomain)
        End Sub

        <Test()> _
        Public Sub getAllArticles()
             ' visuele controle
            listArticles()
        End Sub

        <Test()> _
        Public Sub getArticleById()
             ' artikelen verwijderen
            articlesDao.clearAllArticles()
             ' controle
            Dim articles As IList = articlesDomain.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' 2 artikelen toevoegen
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             ' controle
            articles = articlesDomain.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' artikel 3 zoeken
            Dim unArticle As Article = articlesDomain.getArticleById(3)
             ' controle
            Assert.AreEqual(unArticle.nom, "article3")
             ' artikel 4 zoeken
            unArticle = articlesDao.getArticleById(4)
             ' controle
            Assert.AreEqual(unArticle.nom, "article4")
        End Sub

        <Test()> _
        Public Sub acheterPanier()
             ' artikelen verwijderen
            articlesDao.clearAllArticles()
             ' controle
            Dim articles As IList = articlesDomain.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' 2 artikelen toevoegen
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             ' controle
            articles = articlesDomain.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' aanmaken van een winkelmandje met twee aankopen
            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))
             ' controles
            Assert.AreEqual(700, panier.totalPanier, 0.000001)
            Assert.AreEqual(2, panier.achats.Count)
             ' winkelwagen bevestigen
            articlesDomain.acheter(panier)
             ' controles
            Assert.AreEqual(0, articlesDomain.erreurs.Count)
            Assert.AreEqual(0, panier.achats.Count)
             ' artikel zoeken 3
            Dim unArticle As Article = articlesDomain.getArticleById(3)
             ' controle
            Assert.AreEqual(unArticle.stockactuel, 20)
             ' artikel zoeken 4
            unArticle = articlesDao.getArticleById(4)
             ' controle
            Assert.AreEqual(unArticle.stockactuel, 30)
             ' nieuw winkelmandje
            panier.ajouter(New Achat(New Article(3, "article3", 30, 30, 3), 100))
             ' winkelwagen bevestigen
            articlesDomain.acheter(panier)
             ' controles
            Assert.AreEqual(1, articlesDomain.erreurs.Count)
             ' artikel zoeken 3
            unArticle = articlesDomain.getArticleById(3)
             ' controle
            Assert.AreEqual(unArticle.stockactuel, 20)
        End Sub

        <Test()> _
        Public Sub testRetirerAchats()
             ' inhoud van ARTICLES verwijderen
            articlesDao.clearAllArticles()
             ' leest de tabel ARTICLES
            Dim articles As IList = articlesDao.getAllArticles()
            Assert.AreEqual(0, articles.Count)
             ' invoegen
            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)
             ' leest de tabel ARTICLES
            articles = articlesDomain.getAllArticles()
            Assert.AreEqual(2, articles.Count)
             ' aanmaken van een winkelmandje met twee aankopen
            Dim monPanier As New Panier
            monPanier.ajouter(New Achat(article3, 10))
            monPanier.ajouter(New Achat(article4, 10))
             ' controles
            Assert.AreEqual(700.0, monPanier.totalPanier, 0.000001)
            Assert.AreEqual(2, monPanier.achats.Count)
             ' een reeds gekocht artikel toevoegen
            monPanier.ajouter(New Achat(article3, 10))
             ' controles
             ' het totaal moet worden aangepast naar 1000
            Assert.AreEqual(1000.0, monPanier.totalPanier, 0.000001)
             ' nog steeds 2 artikelen in het winkelmandje
            Assert.AreEqual(2, monPanier.achats.Count)
             ' aantal van artikel 3 moet worden aangepast naar 20
            Dim unAchat As Achat = CType(monPanier.achats(0), Achat)
            Assert.AreEqual(20, unAchat.qte)
             ' artikel 3 wordt uit het winkelmandje verwijderd
            monPanier.enlever(3)
             ' controles
             ' het totaal moet zijn aangepast naar 400
            Assert.AreEqual(400.0, monPanier.totalPanier, 0.000001)
             ' slechts 1 artikel in het winkelmandje
            Assert.AreEqual(1, monPanier.achats.Count)
             ' dit moet artikel nr. 4 zijn
            Assert.AreEqual(4, CType(monPanier.achats(0), Achat).article.id)
        End Sub

         ' schermweergave
        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

Opmerkingen:

  • we wilden een testprogramma schrijven voor de interface [IArticlesDomain] dat onafhankelijk is van de implementatieklasse ervan. Daarom hebben we Spring gebruikt om de naam van de implementatieklasse voor het testprogramma te verbergen.
  • De attribuutmethode <Setup()> haalt bij Spring een verwijzing op naar de te testen objecten [articlesdomain] en [articlesdao]. Deze zijn gedefinieerd in het volgende bestand [spring-config.xml]:
<?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>

Dit bestand geeft het volgende weer:

  • (vervolg)
    • voor het singleton [articlesdao]: de naam van de implementatieklasse [istia.st.articles.dao.ArticlesDaoArrayList] en waar deze te vinden is: [ webarticles-dao.dll]. Aangezien het instantiëren geen parameters vereist, zijn hier geen parameters gedefinieerd.
    • voor het singleton [articlesdomain], de naam van de implementatieklasse [istia.st.articles.domain.AchatsArticles] en waar deze te vinden is: [ webarticles-domain.dll]. De klasse [AchatsArticles] heeft een constructor met één parameter: het singleton dat de toegang tot de laag [dao] beheert. Hier wordt dit gedefinieerd als het eerder gedefinieerde singleton [articlesdao].
  • De testklasse verkrijgt een instantie van de te testen klasse [articlesdomain] en een instantie van de klasse voor gegevenstoegang [articlesdao]. Dit laatste punt is omstreden. De testklasse zou in theorie geen toegang moeten hebben tot de laag [dao], die ze niet eens zou mogen kennen. Hier hebben we deze „ethiek“ terzijde geschoven; als we ons hieraan hadden gehouden, zouden we nieuwe methoden in onze interface [IArticlesDomain] hebben moeten aanmaken.

Om de laag [domain] te testen, genereren we de DLL en [tests-webarticles-domain.dll] in de map [bin] van het project [tests]:

Vervolgens laden we met behulp van de applicatie [Nunit-Gui] dit bestand DLL en voeren we de tests uit:

Image

De lezer die dit document op het scherm bekijkt, zal zien dat alle tests zijn geslaagd. Vervolgens gaan we ervan uit dat we een operationele [domain]-laag hebben.

1.4.5. Conclusie

Ter herinnering: we willen de volgende webapplicatie met drie lagen bouwen:

Het M-model van onze applicatie MVC is nu geschreven en getest. Het wordt ons geleverd in twee bestanden: DLL en [webarticles-dao.dll, webarticles-domain.dll]. We kunnen nu doorgaan naar de laatste laag, de [web]-laag, die de C-controller en de V-weergaven bevat. We zullen eerst een methode bekijken die wordt beschreven in het document [Développement WEB avec ASP.NET 1.1 ]

  • de C-controller wordt verzorgd door twee bestanden [global.asax, main.aspx]
  • De V-weergaven worden verzorgd door aspx-pagina’s

1.5. De laag [web]

De architectuur MVC van de webapplicatie ziet er als volgt uit:

M=modèle
de businessklassen [domain], de klassen voor gegevenstoegang [dao] en de gegevensbron
V=vues
de pagina’s ASPX
C=contrôleur
alle verzoeken van de clients HTTP worden door de volgende twee controllers verwerkt:
global.asax: verwerkt de gebeurtenissen die verband houden met de eerste start van de applicatie
main.aspx: verwerkt de verzoeken van elke klant afzonderlijk

1.5.1. De weergaven

De weergaven komen overeen met die welke aan het begin van dit document zijn gepresenteerd:

LISTE
liste.aspx
De weergaven zijn verzameld in de map [vues] van de applicatie
INFOS
infos.aspx
PANIER
panier.aspx
PANIERVIDE
paniervide.aspx
ERREURS
erreurs.aspx

1.5.2. De controllers

Zoals aangegeven, bestaat de controller uit twee onderdelen:

  1. [global.asax,global.asax.vb]: voornamelijk gebruikt om de applicatie te initialiseren en alle gegevens die tussen de verschillende clients moeten worden gedeeld in de context van de applicatie te plaatsen
  2. [main.aspx, main.aspx.vb]: de eigenlijke controller, die de verzoeken HTTP van de clients verwerkt.

De verschillende verzoeken van de clients worden gericht aan de controller [main.aspx] en bevatten een parameter met de naam [action], die de door de client gevraagde actie specificeert:

verzoek
betekenis
actie van de controller
mogelijke antwoorden
action=liste
de klant wil de lijst met
artikelen
- vraagt de lijst met artikelen aan de
bedrijfslaag
- [LISTE]
- [ERREURS]
action=infos
de klant vraagt om
informatie over een van de
artikelen die in de weergave
[LISTE]
- vraagt het artikel op bij de bedrijfslaag
- [INFOS]
- [ERREURS]
action=achat
de klant koopt een artikel
- vraagt het artikel op bij de bedrijfslaag
en voegt het toe aan het winkelmandje van de klant
- [INFOS] bij een fout in de hoeveelheid
- [LISTE] indien geen fout
action=retirerachat
de klant wil een
aankoop uit zijn winkelmandje verwijderen
- haalt het winkelmandje op uit de sessie
en wijzigt deze
- [PANIER]
- [PANIERVIDE]
- [ERREURS]
action=panier
de klant wil zijn
winkelwagen
- haalt het winkelmandje op uit de sessie
- [PANIER]
- [PANIERVIDE]
- [ERREURS]
action=validationpanier
de klant heeft zijn aankopen afgerond
en gaat over naar de betaalfase
- werkt de voorraad van de gekochte artikelen bij in de database
van de gekochte artikelen
- verwijdert de artikelen
waarvan de aankoop is bevestigd
- [LISTE]
- [ERREURS]

1.5.3. Configuratie van de applicatie

We zullen de applicatie zo configureren dat deze zo flexibel mogelijk is ten aanzien van wijzigingen zoals:

  1. het wijzigen van de URL van de verschillende weergaven
  2. het wijzigen van de klassen die de interfaces [IArticlesDao] en [IArticlesDomain] implementeren
  3. het wijzigen van de SGBD, de database en de artikeltabel

1.5.3.1. De wijzigingen in URL

De namen van de weergaven URL worden samen met enkele andere parameters in het configuratiebestand [web.config] van de applicatie geplaatst:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
..
    <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>

1.5.3.2. De wijziging van de implementatieklassen van de interfaces

In de geest van drielaagse architecturen moeten de lagen onderling afgeschermd zijn. Deze afscherming wordt als volgt bereikt:

  • de lagen communiceren met elkaar via interfaces en niet via concrete klassen
  • de code van een laag maakt nooit zelf een instantie van de klasse van een andere laag om deze te gebruiken. Ze vraagt simpelweg aan een extern hulpmiddel, in dit geval Spring, om een implementatie-instantie van de interface van de laag die ze wil gebruiken. Hiervoor hoef ze de naam van de implementatieklasse niet te kennen, maar alleen de naam van het Spring-singleton waarnaar ze een verwijzing wil.

In onze applicatie wordt Spring in het bestand [web.config] van de webapplicatie als volgt geconfigureerd:

<?xml version="1.0" encoding="iso-8859-1" ?>
<configuration>
    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>
    <spring>
        <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
            <resource uri="config://spring/objects" />
        </context>
        <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>
    </spring>
        <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>

Om toegang te krijgen tot de laag [métier], kan een klasse van de laag [web] het singleton [articlesDomain] opvragen. Spring zal dan een object van het type [istia.st.articles.domain.AchatsArticles] instantiëren. Voor deze instantiëring heeft het een object van het type [articlesDao] nodig, dat wil zeggen een object van het type [istia.st.articles.dao.ArticlesDaoArrayList]. Spring zal vervolgens een dergelijk object instantiëren. Aan het einde van de bewerking beschikt de laag [web], die het singleton [articlesDomain] heeft aangevraagd, over de volledige keten die haar met de gegevensbron verbindt:

1.5.3.3. Wijzigingen met betrekking tot SGBD of de database

Dit punt wordt hier buiten beschouwing gelaten, aangezien we ons in een testomgeving bevinden zonder SGBD. In een tweede fase zullen we ingaan op de implementatie van een [dao]-laag die is gebaseerd op een SGBD.

1.5.4. De <asp:>-tagbibliotheek

Laten we eens kijken naar de weergave [ERREURS] die een lijst met fouten weergeeft:

Image

De weergave [ERREURS] is verantwoordelijk voor het weergeven van een lijst met fouten die de controller [main.aspx] in de verzoekcontext heeft geplaatst onder de naam [context.Items("erreurs")]. Er zijn verschillende manieren om een dergelijke pagina te schrijven. We richten ons hier uitsluitend op het gedeelte waarin de fouten worden weergegeven.

Ter herinnering: een pagina ASPX bestaat uit een presentatiegedeelte HTML en een codegedeelte .NET dat de gegevens voorbereidt die het presentatiegedeelte moet weergeven. Deze twee delen kunnen in één enkel bestand [aspx] staan (oplossing WebMatrix) of in twee bestanden: [aspx] voor de weergave, [aspx.vb] voor de code. Deze laatste oplossing is die van Visual Studio. Om de zaken nog ingewikkelder te maken, kan het presentatiegedeelte HTML ook .NET-code bevatten, wat de scheiding tussen [contrôleur] en [présentation] in de weergave vertroebelt. Van deze oplossing wordt over het algemeen sterk afgeraden. Om alle code uit het gedeelte [présentation] te verwijderen, moesten er tagbibliotheken worden aangemaakt. Deze ‘verbergen’ de code onder de vorm van tags die lijken op de HTML-tags. We presenteren twee mogelijke oplossingen voor de pagina [ERREURS].

Onze eerste oplossing maakt gebruik van de code .NET in het gedeelte [présentation] van de pagina. De pagina ASPX haalt de lijst met fouten uit het verzoek op in het controllergedeelte [erreurs.aspx.vb]:

        Protected erreurs As ArrayList

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
             ' de fouten worden opgehaald
            erreurs = CType(context.Items("erreurs"), ArrayList)
        End Sub

en geeft dit vervolgens weer in het gedeelte [présentation, erreurs.aspx]:

                <h2>Les erreurs suivantes se sont produites :</h2>
                <ul>
                <%
                    for i as integer=0 to erreurs.count-1
                        response.write("<li>" & erreurs(i).ToString & "</li>")
                    next
                %>
                </ul>

De tweede oplossing maakt gebruik van de tag <asp:repeater> uit de <asp:>-tagbibliotheek van ASP.NET. Als men een pagina ASPX grafisch opbouwt, is deze tag beschikbaar in de vorm van een servercomponent die men op het ontwerpformulier kan plaatsen. Als men de code ASPX handmatig opstelt, kan men spreken van een tagbibliotheek.

Met de <asp:>-tagbibliotheek wordt de code ASPX van de vorige weergave [ERREURS] als volgt:

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

De tag

        <asp:Repeater id="rptErreurs" runat="server">

wordt gebruikt om een patroon HTML te herhalen op de verschillende elementen van een gegevensbron. De verschillende elementen zijn als volgt:

HeaderTemplate
het patroon HTML dat moet worden weergegeven voordat de elementen van de gegevensbron worden weergegeven
ItemTemplate
het patroon HTML dat voor elk element van de gegevensbron moet worden herhaald. De uitdrukking [<%# Container.DataItem %>] wordt gebruikt om de waarde van het huidige element van de gegevensbron weer te geven
FooterTemplate
het patroon HTML dat moet worden weergegeven nadat de elementen van de gegevensbron zijn weergegeven

De gegevensbron wordt meestal in het gedeelte [contrôleur] van de pagina aan de tag gekoppeld:

        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
..
             ' de fouten worden gekoppeld aan rptErreurs
            With rptErreurs
                .DataSource = context.Items("erreurs")
                .DataBind()
            End With
        End Sub

Deze koppeling kan ook al tijdens het ontwerpen van de pagina worden gemaakt als de gegevensbron al bekend is, bijvoorbeeld een bestaande database.

In onze weergaven gebruiken we een andere tag: <asp:datagrid>, waarmee een gegevensbron in de vorm van een tabel kan worden weergegeven.

1.5.5. Structuur van de Visual Studio-oplossing van de applicatie [webarticles]

Een webtoepassing is een puzzel met talrijke elementen. Door er een architectuur aan toe te voegen, neemt het aantal elementen doorgaans toe. De structuur van de toepassing [webarticles] onder [Visual Studio] is als volgt:

  

Opmerkingen:

  • het project [web] is van het type [bibliothèque de classes] en niet van het type [Application web ASP.NET], zoals men logischerwijs zou verwachten. Het type [Application web ASP.NET] vereist dat de webserver IIS aanwezig is op de ontwikkelingsmachine of op een externe machine. De server IIS is standaard niet aanwezig op Windows-computers met de versie XP Home. Toch worden veel computers met de versie PC met deze versie verkocht. Om lezers met Windows XP in staat te stellen de besproken toepassing te implementeren, zullen we gebruikmaken van de Cassini-webserver (zie bijlage), die gratis beschikbaar is bij Microsoft, en zullen we het project [Application web ASP.NET] vervangen door een project [bibliothèque de classes]. Dit brengt enkele nadelen met zich mee, die in de bijlagen worden toegelicht.
  • De door de applicatie gebruikte DLL-bestanden zijn de volgende:
webarticles-dao.dll
bevat de klassen van de gegevenslaag
webarticles-domain.dll
bevat de klassen van de bedrijfslaag
Spring.Core.dll
bevat de Spring-klassen waarmee we de web-, domein- en DAO-lagen kunnen integreren
log4net.dll
logklassen – gebruikt door Spring

Deze DLL worden in de map [bin] geplaatst en toegevoegd aan de projectreferenties.

1.5.6. De weergaven ASPX

Zoals eerder aanbevolen, zullen we de <asp:>-tagbibliotheek gebruiken in onze weergaven ASPX.

1.5.6.1. De gebruikerscomponent [entete.ascx]

Om de verschillende weergaven een zekere uniformiteit te geven, zullen deze dezelfde koptekst delen, namelijk die waarin de naam van de applicatie en het menu worden weergegeven:

Het menu is dynamisch en wordt bepaald door de controller. Deze voegt in de verzoek die naar de pagina ASPX wordt verzonden, een sleutelattribuut "actions" toe met als bijbehorende waarde een array van elementen van het type Hashtable(). Elk element van deze array is een woordenboek dat bedoeld is om een optie in het koptekstmenu te genereren. Elk woordenboek heeft twee sleutels:

  • href: de URL die bij de menuoptie hoort
  • link: de tekst van het menu

We maken van de koptekst een gebruikersbesturingselement. Een gebruikersbesturingselement kapselt een deel van de pagina (weergave en bijbehorende code) in een component die vervolgens in andere pagina's kan worden hergebruikt. Hier willen we de component [entete] hergebruiken in de andere weergaven van de applicatie. De presentatiecode komt in [entete.ascx] en de bijbehorende besturingscode in [entete.ascx.vb]. De presentatiecode gebruikt een <asp:repeater>-component om de tabel met menuopties weer te geven:

type
nom
rôle
1
repeater
rptMenu
gegevensbron: een tabel met woordenboeken
met twee sleutels: href, link
menuopties weergeven

De opmaakcode van de pagina ziet er als volgt uit:

<%@ Control codebehind="entete.ascx.vb" Language="vb" autoeventwireup="false" inherits="istia.st.articles.web.EnteteWebArticles" %>
        <table>
            <tr>
                <td>
                    <h2>Magasin virtuel</h2></td>
                <asp:Repeater id="rptMenu" runat="server">
                    <ItemTemplate>
                        <td>
                            |<a href='<%# Container.DataItem("href") %>'>
                                <%# Container.DataItem("link") %>
                            </a>
                        </td>
                    </ItemTemplate>
                </asp:Repeater>
            </tr>
        </table>
        <hr>

Opmerkingen:

  • de component [repeater] wordt gedefinieerd in de regels 6-14
  • elk element van de gegevensbron die aan de repeater is gekoppeld, is een woordenboek met twee sleutels: href - regel 9 en link - regel 10

De bijbehorende controlecode is als volgt:

Namespace istia.st.articles.web
    Public Class EnteteWebArticles
        Inherits System.Web.UI.UserControl

        Protected WithEvents rptMenu As System.Web.UI.WebControls.Repeater

        Public WriteOnly Property actions() As Hashtable()
            Set(ByVal Value As Hashtable())
                ' de actietabel wordt aan de bijbehorende component gekoppeld
                With rptMenu
                    .DataSource = Value
                    .DataBind()
                End With
            End Set
        End Property
    End Class
End Namespace

Opmerkingen:

  • de component van het type [EnteteWebArticles] heeft een openbare eigenschap [actions] die alleen-schrijven is – regel 7
  • met deze eigenschap kan de door de applicatiecontroller berekende optietabel – regels 11-12 – worden gekoppeld aan de <asp:repeater>-component met de naam [rptMenu] – regel 10.

De andere weergaven van de applicatie zullen de door [entete.ascx] gedefinieerde koptekst gebruiken. De pagina [erreurs.aspx] zal bijvoorbeeld de koptekst opnemen met behulp van de volgende code:

<%@ 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>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h3>Les erreurs suivantes se sont produites :
        </h3>    

Opmerkingen:

  • Regel 1 geeft aan dat de tag <WA:entete> moet worden gekoppeld aan de component die is gedefinieerd in het bestand [entete.ascx]. De attributen [TagPrefix] en [TagName] kunnen vrij worden gekozen.
  • Zodra dit is gebeurd, wordt de component met regel 9 in de presentatiecode van de pagina ingevoegd. Bij uitvoering zorgt deze tag ervoor dat de code van de pagina ASPX, waarin deze tag is opgenomen, de code van de pagina [entete.ascx] opneemt. De besturingscode [erreurs.aspx.vb] zorgt ervoor dat deze component wordt geïnitialiseerd. Dit kan op de volgende manier gebeuren:
    Public Class ErreursWebarticles
        Inherits System.Web.UI.Page

         ' paginaonderdelen
...
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
             ' de menuopties worden gekoppeld aan rptmenu
            entete.actions = CType(context.Items("options"), Hashtable())
...
        End Sub
    End Class

Opmerkingen:

  • regel 6 maakt een object van het type [EnteteWebArticles] aan, wat het type is van de aangemaakte component
  • regel 11 initialiseert de eigenschap [actions] van dit object

1.5.6.2. De weergave [liste.aspx]

1.5.6.2.1. Introduction

Deze weergave toont de lijst met artikelen die te koop zijn:

Deze wordt weergegeven na een verzoek /main?action=lijst of /main?action=winkelwagenbevestiging. De elementen van het verzoek aan de controller zijn als volgt:

actions
Hashtable()-object – de lijst met menuopties
listarticles
ArrayList objecten van het type [Article]
message
String-object – bericht dat onderaan de pagina moet worden weergegeven

Elke link [Infos] uit de tabel HTML met artikelen heeft een URL in de vorm [?action=infos&id=ID], waarbij ID het id-veld is van hetweergegeven artikel.

1.5.6.2.2. Paginaonderdelen
type
nom
rôle
1
gebruikerscomponent
koptekst
koptekst weergeven
2
DataGrid
DataGridArticles
3 - gerelateerde kolom: koptekst: Naam, veld: naam
4 - gerelateerde kolom: koptekst: Prijs, veld: prijs
5 - hypertekstkolom: tekst: Info, URL-veld: id,
formaat URL: /webarticles/main.aspx?action=infos&id={0}
artikelen te koop weergeven
6
label
lblMessage
een bericht weergeven

Laten we nog eens bekijken hoe u deze eigenschappen kunt instellen:

  • in Visual Studio selecteer je [DataGrid] om toegang te krijgen tot het eigenschappenvenster:

Image

  • gebruik de bovenstaande link [Mise en forme automatique] om de vorm van de weergegeven tabel te beheren
  • en de link [Générateur de propriétés] om de inhoud ervan te beheren
1.5.6.2.3. Weergavecode [liste.aspx]
<%@ Page codebehind="liste.aspx.vb" inherits="istia.st.articles.web.ListeWebarticles" autoeventwireup="false" Language="vb" %>
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx"%>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h2>Liste des articles</h2>
        <P>
            <asp:DataGrid id="DataGridArticles" runat="server" ForeColor="Black" BackColor="LightGoldenrodYellow"
                BorderColor="Tan" CellPadding="2" BorderWidth="1px" GridLines="None" AutoGenerateColumns="False">
                <SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
                <AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
                <HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
                <FooterStyle BackColor="Tan"></FooterStyle>
                <Columns>
                    <asp:BoundColumn DataField="nom" HeaderText="Nom"></asp:BoundColumn>
                    <asp:BoundColumn DataField="prix" HeaderText="Prix" DataFormatString="{0:C}"></asp:BoundColumn>
                    <asp:HyperLinkColumn Text="Infos" DataNavigateUrlField="id" DataNavigateUrlFormatString="/webarticles/main.aspx?action=infos&amp;id={0}"></asp:HyperLinkColumn>
                </Columns>
                <PagerStyle HorizontalAlign="Center" ForeColor="DarkSlateBlue" BackColor="PaleGoldenrod"></PagerStyle>
            </asp:DataGrid></P>
        <P>
            <asp:Label id="lblMessage" runat="server" BackColor="#FFC080"></asp:Label></P>
    </body>
</HTML>

Opmerkingen:

  • regel 9 definieert de koptekst van de pagina
  • de regels 12-24 definiëren de kenmerken van [DataGrid]
  • regel 26 definieert het label [lblMessage]
1.5.6.2.4. Controlecode [liste.aspx.vb]
Imports System
Imports System.Collections
Imports System.Data
Imports istia.st.articles.dao

Namespace istia.st.articles.web

     ' beheert de pagina waarop de artikelenlijst wordt weergegeven
    Public Class ListeWebarticles
        Inherits System.Web.UI.Page

         ' paginaonderdelen
        Protected WithEvents lblMessage As System.Web.UI.WebControls.Label
        Protected WithEvents DataGridArticles As System.Web.UI.WebControls.DataGrid
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             ' de weergave [liste] wordt opgesteld op basis van de contextinformatie
             ' de menuopties worden gekoppeld aan rptmenu
            entete.actions = CType(context.Items("options"), Hashtable())
             ' de artikelen worden opgehaald in een datatable
            Dim articles As ArrayList = CType(context.Items("articles"), ArrayList)
             ' deze worden gekoppeld aan de component [DataGrid] van de pagina
            With DataGridArticles
                .DataSource = articles
                .DataBind()
            End With
             ' het bericht wordt weergegeven
            lblMessage.Text = context.Items("message").ToString
        End Sub
    End Class
End Namespace

Opmerkingen:

  • De componenten van de pagina staan in de regels 13-15. Merk op dat we een object [EnteteWebArticles] moesten aanmaken met een operator [new], terwijl dit bij de andere componenten niet nodig was. Zonder deze expliciete aanmaak kregen we een uitvoeringsfout die aangaf dat het object [entete] nergens naar verwees. Dit punt zou nader onderzocht moeten worden. Dat is niet gebeurd.
  • de tabel met menuopties in de koptekst wordt uit de context gehaald om de component [entete] van de pagina te initialiseren – regel 20
  • de lijst met artikelen wordt uit de context gehaald – regel 22
  • om de component [DataGridArticles] te initialiseren – regels 24-27
  • het onderdeel [lblMessage] wordt geïnitialiseerd met een bericht dat in de context is geplaatst – regel 29

1.5.6.3. De weergave [infos.aspx]

1.5.6.3.1. Introduction

Deze weergave toont informatie over een artikel en maakt het ook mogelijk om het te kopen:

Image

Deze wordt weergegeven na een verzoek /main?action=infos&id=ID of een verzoek /main?action=achat&id=ID wanneer de gekochte hoeveelheid onjuist is. De elementen van het verzoek van de controller zijn als volgt:

actions
Hashtable()-object – de lijst met menuopties
article
object van het type [Article] – weer te geven artikel
msg
String-object - bericht dat moet worden weergegeven bij een fout in de hoeveelheid
qte
String-object - waarde die in het invoerveld moet worden weergegeven [Qte]

De velden [msg] en [qte] worden gebruikt bij een invoerfout in de hoeveelheid:

Image

Deze pagina bevat een formulier dat wordt verzonden via de knop [Acheter]. De bestemming van URL vanuit POST is [?action=achat&id=ID], waarbij ID de ID is van het gekochte artikel.

1.5.6.3.2. De onderdelen van de pagina
type
nom
rôle
1
gebruikerscomponent
koptekst
koptekst weergeven
2
letterlijke waarde
litID
artikelnummer weergeven
3 à 6
DataGrid
DataGridArticle
3 - gerelateerde kolom: koptekst: Naam, veld: naam
4 - gerelateerde kolom: koptekst: Prijs, veld: prijs
5 - gerelateerde kolom: koptekst: Huidige voorraad, veld: stockActuel
6 - gerelateerde kolom: koptekst: Minimale voorraad, veld: stockMinimum
een artikel weergeven
7
HTML Verzenden
 
formulier verzenden
8
HTML Input
runat=server
txtQte
de gekochte hoeveelheid invoeren
9
label
lblMsgQte
eventuele foutmelding
1.5.6.3.3. De presentatiecode [infos.aspx]
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %>
<%@ Page codebehind="infos.aspx.vb" inherits="istia.st.articles.web.InfosWebarticles" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h2>Article d'id [<asp:Literal id="litId" runat="server"></asp:Literal>]</h2>
        <P>
            <asp:DataGrid id="DataGridArticle" runat="server" BackColor="White" BorderColor="#E7E7FF" CellPadding="3"
                BorderWidth="1px" BorderStyle="None" GridLines="Horizontal" AutoGenerateColumns="False">
                <SelectedItemStyle Font-Bold="True" ForeColor="#F7F7F7" BackColor="#738A9C"></SelectedItemStyle>
                <AlternatingItemStyle BackColor="#F7F7F7"></AlternatingItemStyle>
                <ItemStyle HorizontalAlign="Center" ForeColor="#4A3C8C" BackColor="#E7E7FF"></ItemStyle>
                <HeaderStyle Font-Bold="True" HorizontalAlign="Center" ForeColor="#F7F7F7" BackColor="#4A3C8C"></HeaderStyle>
                <FooterStyle ForeColor="#4A3C8C" BackColor="#B5C7DE"></FooterStyle>
                <Columns>
                    <asp:BoundColumn DataField="nom" HeaderText="Nom">
                        <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                    </asp:BoundColumn>
                    <asp:BoundColumn DataField="prix" HeaderText="Prix" DataFormatString="{0:C}">
                        <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                    </asp:BoundColumn>
                    <asp:BoundColumn DataField="stockactuel" HeaderText="Stock actuel">
                        <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                    </asp:BoundColumn>
                    <asp:BoundColumn DataField="stockminimum" HeaderText="Stock minimum">
                        <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                    </asp:BoundColumn>
                </Columns>
                <PagerStyle HorizontalAlign="Right" ForeColor="#4A3C8C" BackColor="#E7E7FF" Mode="NumericPages"></PagerStyle>
            </asp:DataGrid></P>
        <HR width="100%" SIZE="1">
        <form method="post" action="<%=strAction%>">
            <table>
                <tr>
                    <td><input type="submit" value="Acheter"></td>
                    <td>Qté</td>
                    <td><INPUT type="text" maxLength="3" size="3" id="txtQte" runat="server"></td>
                    <td><asp:Label id="lblMsgQte" runat="server" />
                    </td>
                </tr>
            </table>
        </form>
    </body>
</HTML>

Opmerkingen:

  • de koptekst is opgenomen in de pagina - regel 9
  • de letterlijke waarde [litId] is gedefinieerd op regel 10
  • de DataGrid [DataGridArticles] wordt gedefinieerd op regels 12-34
  • het formulier wordt gedefinieerd op de regels 36-46. Het heeft het type POST.
  • het doel van POST wordt geleverd door een variabele [strAction] – regel 36. Deze variabele moet door de controller worden gedefinieerd.
  • Het invoerveld voor de gekochte hoeveelheid is gedefinieerd in regel 41. Dit is een servercomponent HTML (runat=server). In de code is deze toegankelijk via een object.
  • Op regel 42 wordt het label [lblMsgQte] gedefinieerd, dat een eventuele foutmelding over de ingevoerde hoeveelheid zal bevatten
1.5.6.3.4. De controlecode [infos.aspx.vb]
Imports istia.st.articles.dao
Imports System
Imports System.Collections

Namespace istia.st.articles.web

     ' beheert de informatiepagina over een artikel
    Public Class InfosWebarticles
        Inherits System.Web.UI.Page
        Protected WithEvents lblMsgQte As System.Web.UI.WebControls.Label
        Protected WithEvents litId As System.Web.UI.WebControls.Literal
        Protected WithEvents txtQte As System.Web.UI.HtmlControls.HtmlInputText
        Protected WithEvents DataGridArticle As System.Web.UI.WebControls.DataGrid
        Protected WithEvents entete As New EnteteWebArticles

         ' de URL waar het formulier naartoe wordt verzonden
        Private _strAction As String
        Public Property strAction() As String
            Get
                Return _strAction
            End Get
            Set(ByVal Value As String)
                _strAction = Value
            End Set
        End Property

         ' pagina weergeven
        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             ' de gegevens van de aanvraag worden opgehaald
            Dim unArticle As Article = CType(Session.Item("article"), Article)
             ' de menuopties worden gekoppeld aan rptmenu
            entete.actions = CType(context.Items("options"), Hashtable())
             ' het artikel wordt gekoppeld aan [DataGrid]
            Dim articles As New ArrayList
            articles.Add(unArticle)
            With DataGridArticle
                .DataSource = articles
                .DataBind()
            End With
             ' het label-id
            litId.Text = unArticle.id.ToString
             ' de foutmelding
            lblMsgQte.Text = context.Items("msg").ToString
             ' de vorige hoeveelheid
            txtQte.Value = context.Items("qte").ToString
             ' de actie
            strAction = "?action=achat&id=" + unArticle.id.ToString
        End Sub
End Namespace

Opmerkingen:

  • de componenten van de pagina worden gedefinieerd in de regels 10-14
  • De klasse definieert een openbare eigenschap [strAction] die dient om het doel van POST van het formulier te definiëren – regels 17-25
  • het weer te geven artikel wordt opgehaald uit de context van de applicatie – regel 30
  • de tabel met opties voor het koptekstmenu wordt uit de context gehaald om de component [entete] van de pagina te initialiseren – regel 32
  • regels 33-39: de component [DataGridArticle] wordt gekoppeld aan een gegevensbron van het type [ArrayList] die uitsluitend het in regel 30 opgehaalde artikel bevat
  • de componenten [lblMsgQte, txtQte] worden geïnitialiseerd met informatie uit de context – regels 42-45
  • de eigenschap [straction] wordt eveneens geïnitialiseerd met informatie uit de context – regel 47. Deze variabele wordt gebruikt om het attribuut [action] te genereren van het formulier HTML dat op de pagina staat:
        <form method="post" action="<%=strAction%>">
....
        </form>

1.5.6.4. De weergave [panier.aspx]

1.5.6.4.1. Introduction

Deze weergave toont de inhoud van het winkelmandje:

Image

Deze wordt weergegeven na een verzoek /main?action=panier of /main?action=retirerachat&id=ID. De elementen van het verzoek aan de controller zijn als volgt:

actions
Hashtable()-object – de lijst met menuopties
panier
object van het type [Panier] – het weer te geven winkelmandje

Elke koppeling [Retirer] in de tabel HTML met de aankopen in het winkelmandje heeft een URL in de vorm [?action=retirerachat&id=ID], waarbij ID het veld [id] van het artikel dat uit het winkelmandje moet worden verwijderd.

1.5.6.4.2. De onderdelen van de pagina
type
nom
rôle
1
gebruikerscomponent
koptekst
koptekst weergeven
2
DataGrid
DataGridAchats
3 - gerelateerde kolom - koptekst: Artikel, veld: naam
4 - gerelateerde kolom - koptekst: Aantal, veld: aantal
5 - gekoppelde kolom - koptekst: Prijs, veld: prijs
6 - gekoppelde kolom - koptekst: Totaal, veld: totaal, opmaak {0:C}
7 - hypertekstkolom - Tekst: Verwijderen, URL: id, URL-formaat: /webarticles/main.aspx?action=retirerachat&id={0}
de lijst met gekochte artikelen weergeven
8
label
lblTotal
het te betalen bedrag weergeven
1.5.6.4.3. De weergavecode [panier.aspx]
<%@ Page codebehind="panier.aspx.vb" inherits="istia.st.articles.web.PanierWebarticles" autoeventwireup="false" Language="vb" %>
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h2>Contenu de votre panier</h2>
        <P>
            <asp:DataGrid id="DataGridAchats" runat="server" BorderWidth="1px" GridLines="Vertical" CellPadding="4"
                BackColor="White" BorderStyle="None" BorderColor="#DEDFDE" ForeColor="Black" AutoGenerateColumns="False">
                <SelectedItemStyle Font-Bold="True" ForeColor="White" BackColor="#CE5D5A"></SelectedItemStyle>
                <AlternatingItemStyle BackColor="White"></AlternatingItemStyle>
                <ItemStyle BackColor="#F7F7DE"></ItemStyle>
                <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#6B696B"></HeaderStyle>
                <FooterStyle BackColor="#CCCC99"></FooterStyle>
                <Columns>
                    <asp:BoundColumn DataField="nom" HeaderText="Article"></asp:BoundColumn>
                    <asp:BoundColumn DataField="qte" HeaderText="Qt&#233;"></asp:BoundColumn>
                    <asp:BoundColumn DataField="prix" HeaderText="Prix"></asp:BoundColumn>
                    <asp:BoundColumn DataField="totalAchat" HeaderText="Total" DataFormatString="{0:C}"></asp:BoundColumn>
                    <asp:HyperLinkColumn Text="Retirer" DataNavigateUrlField="id" DataNavigateUrlFormatString="/webarticles/main.aspx?action=retirerachat&amp;id={0}"></asp:HyperLinkColumn>
                </Columns>
                <PagerStyle HorizontalAlign="Right" ForeColor="Black" BackColor="#F7F7DE" Mode="NumericPages"></PagerStyle>
            </asp:DataGrid></P>
        <P>Total de la commande :
            <asp:Label id="lblTotal" runat="server"></asp:Label>&nbsp;euros</P>
    </body>
</HTML>

Opmerkingen

  • regel 9 bevat de koptekst
  • in de regels 12-27 wordt de component [DataGridAchats] gedefinieerd
  • in regel 29 wordt de component [lblTotal] gedefinieerd
1.5.6.4.4. De controlecode [panier.aspx.vb]
Imports System
Imports System.Collections
Imports System.Data
Imports istia.st.articles.dao
Imports istia.st.articles.domain

Namespace istia.st.articles.web
     ' beheert de weergavepagina van het winkelmandje
    Public Class PanierWebarticles
        Inherits System.Web.UI.Page
        Protected WithEvents DataGridAchats As System.Web.UI.WebControls.DataGrid
        Protected WithEvents lblTotal As System.Web.UI.WebControls.Label
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             ' koppelt de menuopties aan rptmenu
            entete.actions = CType(context.Items("options"), Hashtable())
             ' het winkelmandje wordt opgehaald
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
             ' de aankopen worden overgebracht naar een tabel met aankoopregels
            Dim achats(unPanier.achats.Count - 1) As LigneAchat
             ' het type van de elementen van ArrayList wordt gewijzigd
            For i As Integer = 0 To achats.Length - 1
                achats(i) = New LigneAchat(CType(unPanier.achats(i), Achat))
            Next
             ' de gegevens worden gekoppeld aan de componenten [DataGrid] van de pagina
            With DataGridAchats
                .DataSource = achats
                .DataBind()
            End With
             ' het te betalen totaal wordt weergegeven
            lblTotal.Text = unPanier.totalPanier.ToString
        End Sub

         ' aankoopregel samengesteld op basis van een aankoopobject
        Private Class LigneAchat
            Inherits Achat

             ' de constructor ontvangt een aankoop
            Public Sub New(ByVal unAchat As Achat)
                Me.article = unAchat.article
                Me.qte = unAchat.qte
            End Sub

             ' id: geeft de id van het gekochte artikel weer
            Public ReadOnly Property id() As Integer
                Get
                    Return article.id
                End Get
            End Property

             ' naam: naam van het gekochte artikel
            Public ReadOnly Property nom() As String
                Get
                    Return article.nom
                End Get
            End Property

             ' prijs van het gekochte artikel
            Public ReadOnly Property prix() As Double
                Get
                    Return article.prix
                End Get
            End Property

        End Class
    End Class
End Namespace

Opmerkingen:

  • de componenten van de pagina worden gedeclareerd in de regels 11-13
  • de initialisatie van de component [entete] is identiek aan die in de reeds besproken pagina's – regel 17
  • het weer te geven winkelmandje wordt opgehaald uit de sessie – regel 19
  • het weergeven van dit winkelmandje met behulp van de component [DataGridAchats] levert problemen op. De moeilijkheid zit hem in de initialisatie van de component. Laten we de kolommen ervan nog eens op een rijtje zetten:
    • kolom [Article] gekoppeld aan veld [nom] van de gegevensbron
    • kolom [Qté], gekoppeld aan veld [qte] van de gegevensbron
    • kolom [Prix] gekoppeld aan veld [prix] van de gegevensbron
    • kolom [Total], gekoppeld aan het veld [total] van de gegevensbron

De gegevensbron waarover we beschikken is het winkelmandje en de bijbehorende boodschappenlijst. Deze lijst zal de gegevensbron zijn voor [DataGrid]. Alleen de objecten [Achat] die de rijen van [DataGrid] zullen vullen, beschikken niet over de eigenschappen [nom, qte, prix, total] die door [DataGrid] worden verwacht. Daarom wordt hier, speciaal voor [DataGrid], een gegevensbron aangemaakt waarvan de elementen de kenmerken hebben die door [DataGrid] worden verwacht. Deze elementen zullen van het type [LigneAchat] zijn, een klasse die speciaal voor deze gelegenheid is aangemaakt en is afgeleid van de klasse [Achat] – regels 36-66

  • Zodra de klasse [LigneAchat] is gedefinieerd, wordt de gegevensbron van [DataGridAchats] samengesteld op basis van het winkelmandje dat in de sessie is gevonden – regels 20-30
  • het aankoopbedrag wordt weergegeven via de eigenschap [totalPanier] van de klasse [Panier] – regel 32

1.5.6.5. De weergave [paniervide.aspx]

1.5.6.5.1. Introduction

Deze weergave toont de informatie dat het winkelmandje leeg is:

Image

Dit wordt weergegeven na een verzoek /main?action=winkelwagen of /main?action=aankoop annuleren&id=ID. De elementen van het verzoek aan de controller zijn als volgt:

actions
Hashtable()-object – de tabel met menuopties
1.5.6.5.2. De componenten van de pagina
type
nom
rôle
1
gebruikerscomponent
koptekst
koptekst weergeven
1.5.6.5.3. De weergavecode [paniervide.aspx]
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx"%>
<%@ Page codebehind="paniervide.aspx.vb" inherits="istia.st.articles.web.PaniervideWebarticles" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h2>Contenu de votre panier</h2>
        <P>Votre panier est vide</P>
    </body>
</HTML>

Opmerkingen:

  • de koptekst is opgenomen op regel 9
1.5.6.5.4. De controlecode [paniervide.aspx.vb]
Namespace istia.st.articles.web
     ' beheert de weergavepagina van een leeg winkelmandje
    Public Class PaniervideWebarticles
        Inherits System.Web.UI.Page
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             ' de weergave [paniervide] wordt opgesteld op basis van de contextinformatie
             ' de menuopties worden gekoppeld aan rptmenu
            entete.actions = CType(context.Items("options"), Hashtable())
        End Sub
    End Class
End Namespace

Opmerkingen:

  • we initialiseren alleen de enige dynamische component van de pagina - regel 10

1.5.6.6. De weergave [erreurs.aspx]

1.5.6.6.1. Introduction

Dit scherm wordt weergegeven als er fouten optreden:

Image

Deze wordt weergegeven na elke aanvraag die tot een fout leidt, behalve bij een aankoopactie met een onjuiste hoeveelheid, die wordt afgehandeld door de weergave [INFOS]. De elementen van de aanvraag van de controller zijn als volgt:

actions
object Hashtable() – de lijst met menuopties
erreurs
ArrayList van objecten [String] die de weer te geven foutmeldingen vertegenwoordigen
1.5.6.6.2. De componenten van de pagina
type
nom
rôle
1
gebruikerscomponent
koptekst
koptekst weergeven
2
herhaling
rptErreurs
de foutenlijst weergeven
1.5.6.6.3. De weergavecode [erreurs.aspx]
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %>
<%@ Page codebehind="erreurs.aspx.vb" 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>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h3>Les erreurs suivantes se sont produites :
        </h3>
        <ul>
            <asp:Repeater id="rptErreurs" runat="server">
                <ItemTemplate>
                    <li>
                        <%# Container.DataItem %>
                    </li>
                </ItemTemplate>
            </asp:Repeater></ul>
    </body>
</HTML>

Opmerkingen:

  • de header is gedefinieerd op regel 9
  • De component [rptErreurs] is gedefinieerd op regels 13-19. De inhoud ervan is afkomstig van een gegevensbron van het type [ArrayList] met objecten van het type [String].
1.5.6.6.4. De controlecode [erreurs.aspx.vb]
Namespace istia.st.articles.web

     ' beheert de foutpagina
    Public Class ErreursWebarticles
        Inherits System.Web.UI.Page

         ' paginaonderdelen
        Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             ' de weergave [erreurs] wordt opgesteld op basis van de contextinformatie
             ' de menuopties worden gekoppeld aan rptmenu
            entete.actions = CType(context.Items("options"), Hashtable())
             ' de fouten worden gekoppeld aan rptErreurs
            With rptErreurs
                .DataSource = context.Items("erreurs")
                .DataBind()
            End With
        End Sub
    End Class

End Namespace

Opmerkingen:

  • de component [entete] wordt zoals gebruikelijk geïnitialiseerd, regels 9 en 14
  • de component [rptErreurs] wordt geïnitialiseerd met de foutenlijst van het type [ArrayList] die in de context is aangetroffen – regels 16-19

1.5.7. De controllers global.asax, main.aspx

Nu moeten we nog de kern van onze webapplicatie schrijven: de controller. Deze heeft als taak:

  • het verzoek van de klant ophalen,
  • de door de klant gevraagde actie te verwerken met behulp van de businessklassen,
  • de juiste weergave als antwoord terugsturen.

1.5.7.1. De controller [global.asax.vb]

Wanneer de applicatie haar allereerste verzoek ontvangt, wordt de procedure [Application_Start] uit het bestand [global.asax.vb] uitgevoerd. Dit gebeurt slechts één keer. De procedure [Application_Start] heeft tot doel de objecten te initialiseren die nodig zijn voor de webapplicatie en die door alle client-threads als-alleen-lezen worden gedeeld. Deze gedeelde objecten kunnen op twee plaatsen worden ondergebracht:

  • de privévelden van de controller
  • de uitvoeringscontext van de applicatie (Application)

De methode [Application_Start] van de applicatie [global.asax.vb] voert de volgende acties uit:

  • controleren of de parameters die nodig zijn voor de goede werking van de applicatie aanwezig zijn in het bestand [web.config]. Deze zijn beschreven in paragraaf 1.5.3.
  • voegt de lijst met eventuele fouten in de vorm van een object [ArrayList erreurs] toe aan de context van de applicatie. Deze lijst is leeg als er geen fouten zijn, maar bestaat niettemin.
  • Als er fouten zijn opgetreden, stopt de methode [Application_Start] hier. Anders vraagt deze om de referentie van een singleton van het type [IArticlesDomain], wat het bedrijfsobject is dat de controller voor zijn doeleinden zal gebruiken. Zoals uitgelegd in 1.5.3.2, zal de controller dit singleton opvragen bij het Spring-framework. Deze instantiëring kan verschillende fouten opleveren. Als dat het geval is, worden deze opnieuw opgeslagen in het object [erreurs] van de applicatiecontext.

De controller [global.asax.vb] beschikt over een procedure [Session_Start] die telkens wordt uitgevoerd wanneer er een nieuwe klant binnenkomt. In deze procedure wordt een leeg winkelmandje voor de klant aangemaakt. Dit winkelmandje blijft behouden gedurende alle verzoeken van deze specifieke klant. De code zou er als volgt uit kunnen zien:

Imports System
Imports System.Web
Imports System.Web.SessionState
Imports System.Configuration
Imports istia.st.articles.domain
Imports System.Collections
Imports Spring.Context

Namespace istia.st.articles.web

    Public Class GlobalWebArticles
        Inherits System.Web.HttpApplication

         ' de applicatie wordt geïnitialiseerd
        Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)

             ' lokale gegevens
            Dim parameters() As String = {"urlMain", "urlErreurs", "urlInfos", "urlListe", "urlPanier", "urlPanierVide"}
            Dim erreurs As New ArrayList

             ' de initialisatieparameters van de applicatie worden opgehaald
            Dim param As String
            For i As Integer = 0 To parameters.Length - 1
                 ' uit het configuratiebestand lezen
                param = ConfigurationSettings.AppSettings(parameters(i))
                If param Is Nothing Then
                     ' de fout wordt genoteerd
                    erreurs.Add("Paramètre [" + parameters(i) + "] absent dans le fichier [web.config]")
                Else
                     ' de parameter wordt in de applicatie opgeslagen
                    Application.Item(parameters(i)) = param
                End If
            Next
             ' Zijn er fouten?
            If erreurs.Count = 0 Then
                 ' er wordt een object IArticlesDomain aangemaakt voor toegang tot de bedrijfslaag
                Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
                Dim articlesDomain As IArticlesDomain
                Try
                    articlesDomain = CType(contexte.GetObject("articlesDomain"), IArticlesDomain)
                     ' het object wordt in de applicatie opgeslagen
                    Application.Item("articlesDomain") = articlesDomain
                Catch ex As Exception
                     ' de fout wordt opgeslagen
                    erreurs.Add("Erreur lors de la construction de l'objet d'accès à la couche métier [" + ex.ToString + "]")
                End Try
            End If
             ' de fouten worden in de applicatie geplaatst
            Application.Item("erreurs") = erreurs
             ' het is klaar als er fouten zijn opgetreden
            If erreurs.Count <> 0 Then Return
             ' er wordt een tabel met menuopties samengesteld
            Dim options As New Hashtable
             ' we halen de URL op uit de controller
            Dim urlMain As String = CType(Application.Item("urlMain"), String)
            Dim uneOption As Hashtable
             ' artikellijst
            uneOption = New Hashtable
            uneOption.Add("href", urlMain + "?action=liste")
            uneOption.Add("lien", "Liste des articles")
            options.Add("liste", uneOption)
             ' winkelwagen
            uneOption = New Hashtable
            uneOption.Add("href", urlMain + "?action=panier")
            uneOption.Add("lien", "Voir le panier")
            options.Add("panier", uneOption)
             ' winkelwagen bevestigen
            uneOption = New Hashtable
            uneOption.Add("href", urlMain + "?action=validationpanier")
            uneOption.Add("lien", "Valider le panier")
            options.Add("validationpanier", uneOption)
             ' de menuopties worden in de applicatie geplaatst
            Application.Item("options") = options
            Return
        End Sub

         ' sessie initialiseren
        Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
             ' we maken een winkelmandje aan voor de klant
            Session.Item("panier") = New Panier
        End Sub
    End Class
End Namespace

Opmerkingen:

  • de verwachte parameters in [web.config] worden gedefinieerd in een array – regel 18
  • ze worden opgezocht in [web.config]. Als ze aanwezig zijn, worden ze opgeslagen in de applicatiecontext; anders wordt er een fout geregistreerd in de foutenlijst [erreurs] – regels 21-33
  • als er geen fouten zijn, wordt bij Spring een verwijzing opgevraagd naar het singleton [articlesDomain] dat de toegang tot de laag [domain] van de applicatie beheert – regels 35-47. Eventuele fouten worden vastgelegd in [erreurs].
  • De fouten worden vastgelegd in de context van de applicatie – regel 49
  • de procedure wordt verlaten als er fouten zijn opgetreden – regel 51
  • er wordt een array met drie woordenboeken aangemaakt. Elk daarvan heeft twee sleutels: href en link. Deze array vertegenwoordigt de drie mogelijke menuopties – regels 52-71
  • deze array wordt opgeslagen in de applicatiecontext – regel 73
  • Bij elke nieuwe klant wordt de procedure [Session_Start] uitgevoerd. Daarin wordt een leeg winkelmandje aangemaakt in de sessie van de klant – regels 78-81

1.5.7.2. De controller [main.aspx.vb]

De controller [main.aspx.vb] verwerkt alle verzoeken van klanten. Deze hebben namelijk allemaal de vorm [/webarticles/main.aspx?action=XX]. Een verzoek wordt als volgt verwerkt:

  • het object [erreurs] uit de applicatiecontext wordt gecontroleerd. Als dit niet leeg is, betekent dit dat er fouten zijn opgetreden tijdens het initialiseren van de applicatie en dat deze niet kan functioneren. In dat geval wordt als antwoord de weergave [ERREURS] verzonden.
  • de parameter [action] van het verzoek wordt opgehaald en gecontroleerd. Als deze niet overeenkomt met een bekende actie, wordt de weergave [ERREURS] verzonden met een passend foutbericht.
  • Als de parameter [action] geldig is, wordt het verzoek van de klant doorgegeven aan een actiespecifieke procedure voor verwerking:
methode
verzoek
verwerking
mogelijke antwoorden
doListe
GET /main?action=lijst
- de lijst met artikelen opvragen
bij de businessklasse
- weergeven
[LISTE] of [ERREURS]
doInfos
GET /main?action=infos&id=ID
- het artikel met id=ID opvragen bij
de businessklasse
- het weergeven
[INFOS] of [ERREURS]
doAchat
POST /main?action=aankoop&id=ID
- de gekochte hoeveelheid maakt deel uit van de verzonden parameters
- vraag het artikel met id=ID op bij
de businessklasse
- voeg het toe aan het winkelmandje in
de klantsessie
[LISTE] of [INFOS] of [ERREURS]
doRetirerAchat
GET /main?action=retirerachat&id=ID
- het artikel met id=ID verwijderen uit de
aankooplijst van het winkelmandje van
de klantsessie
[PANIER]
doPanier
GET /main?action=winkelwagen
- het winkelmandje van de
klantsessie
[PANIER] of [PANIERVIDE]
doValidationPanier
GET /main?action=winkelwagenbevestiging
- de
voorraad van alle artikelen
die zich in het winkelmandje van
sessie van de klant
[LISTE] of [ERREURS]

Het raamwerk van de controller [main.aspx.vb] zou er als volgt uit kunnen zien:

Imports System.Collections
Imports System
Imports System.Data

Imports istia.st.articles.dao
Imports istia.st.articles.domain

Namespace istia.st.articles.web

     ' controllerklasse van de webapplicatie
    Public Class MainWebArticles
        Inherits System.Web.UI.Page

         ' privévelden
        Private articlesDomain As IArticlesDomain
        Private options As Hashtable

         ' pagina laden
        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
....
        End Sub

         ' methoden voor het verwerken van acties

         ' artikellijst
        Public Sub doListe()
...
        End Sub

         ' informatie over een artikel
        Public Sub doInfos()
...
        End Sub

         ' een artikel kopen
        Public Sub doAchat()
...
        End Sub

         ' een aankoop verwijderen
        Public Sub doRetirerAchat()
...
        End Sub

         ' winkelwagen bekijken
        Public Sub doPanier()
...
        End Sub

         ' winkelwagen afrekenen
        Public Sub doValidationPanier()
...
        End Sub

    End Class

End Namespace

Opmerkingen:

  • de klasse heeft twee privévelden die door de methoden worden gedeeld – regels 15-16:
    • articlesDomain: de singleton voor toegang tot de laag [domain]
    • opties: de array met woordenboeken van de menuopties
  • de procedure [Page_Load]:
    • initialiseert de twee privévelden van de klasse
    • haalt de parameter [action] uit de aanvraag op en voert de verwerkingsmethode voor deze actie uit.

1.5.7.3. De methode [Page_Load]

Dit is de eerste gebeurtenis die op de pagina plaatsvindt. De code is als volgt:

         ' pagina laden
        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             ' er wordt gecontroleerd of de applicatie correct is opgestart
            Dim erreurs As ArrayList = CType(Application.Item("erreurs"), ArrayList)
             ' als er fouten zijn, wordt de weergave [erreurs] verzonden
            If erreurs.Count <> 0 Then
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {}
                Server.Transfer(CType(Application("urlErreurs"), String))
            End If
             ' het toegangsobject voor de businessklasse wordt opgehaald
            articlesDomain = CType(Application.Item("articlesDomain"), IArticlesDomain)
             ' evenals de menuopties
            options = CType(Application.Item("options"), Hashtable)
             ' we halen de uit te voeren actie op
            Dim action As String = Request.QueryString("action")
            If action Is Nothing Then
                action = "liste"
            End If
             ' de actie wordt uitgevoerd
            Select Case action
                Case "liste"
                    doListe()
                Case "infos"
                    doInfos()
                Case "achat"
                    doAchat()
                Case "panier"
                    doPanier()
                Case "retirerachat"
                    doRetirerAchat()
                Case "validationpanier"
                    doValidationPanier()
                Case Else
                    doListe()
            End Select
        End Sub

Opmerkingen:

  • bij elke paginalading wordt gecontroleerd of de initialisatie van de applicatie door [global.asax] correct is verlopen.
  • Hiervoor wordt in de context van de applicatie de lijst met fouten opgehaald die daar door [global.asax] is geplaatst – regel 4
  • Als deze lijst niet leeg is, wordt de weergave [ERREURS] weergegeven – regels 6-10
  • we halen het singleton [articlesDomain] op dat door [global.asax] in de applicatiecontext is geplaatst en slaan dit op in het privéveld [articlesDomain], zodat het beschikbaar is voor de verschillende methoden van de klasse – regel 12
  • we doen iets soortgelijks met de array met menuopties – regel 14
  • de parameter [action] wordt uit het verzoek opgehaald – regel 16
  • we laten de methode uitvoeren die overeenkomt met de gevraagde actie. Een niet-voorziene actie wordt beschouwd als de actie [liste] – regels 16-36

1.5.7.4. Verwerking van de actie [liste]

Het gaat om het weergeven van de artikellijst:

Image

De code is als volgt:

         ' lijst met artikelen
        Public Sub doListe()
             ' foutbeheer
            Dim erreurs As New ArrayList
             ' de lijst met artikelen opvragen
            Dim articles As IList
            Try
                articles = articlesDomain.getAllArticles
            Catch ex As Exception
                 ' de fout wordt genoteerd
                erreurs.Add(ex.ToString)
            End Try
             ' fouten?
            If erreurs.Count <> 0 Then
                 ' weergave van het overzicht [erreurs]
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
            End If
             ' weergave van het overzicht [liste]
            context.Items("articles") = articles
            context.Items("options") = New Hashtable() {CType(options("panier"), Hashtable)}
            If context.Items("message") Is Nothing Then context.Items("message") = ""
            Server.Transfer(CType(Application.Item("urlListe"), String))
        End Sub

Opmerkingen:

  • eventuele fouten worden opgeslagen in een [ArrayList] - regel 4
  • de lijst met artikelen wordt opgevraagd bij het singleton [articlesDomain] – regels 5-12
  • als er fouten zijn opgetreden, wordt de weergave [ERREURS] verzonden – regels 13-19
  • anders wordt de weergave [LISTE] verzonden – regels 20-24

1.5.7.5. Verwerking van de actie [infos]

De klant heeft informatie aangevraagd over een bepaald artikel:

Image

De code is als volgt:

         ' informatie over een artikel
        Public Sub doInfos()
             ' foutbeheer
            Dim erreurs As New ArrayList
             ' de id van het opgevraagde artikel ophalen
            Dim strId As String = Request.QueryString("id")
             ' is er iets?
            If strId Is Nothing Then
                 ' niet normaal – de foutpagina wordt weergegeven
                erreurs.Add("action incorrecte (action=infos, id=rien)")
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End If
             ' hebben we een geheel getal?
            Dim id As Integer
            Try
                id = Integer.Parse(strId)
            Catch ex As Exception
                 ' niet normaal – we sturen de foutpagina
                erreurs.Add("action incorrecte (action=infos, id[" + strId + "] invalide)")
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End Try
             ' het artikel met sleutel-id wordt opgevraagd
            Dim unArticle As Article
            Try
                unArticle = articlesDomain.getArticleById(id)
            Catch ex As Exception
                 ' probleem met toegang tot de gegevens
                erreurs.Add("Problème d'accès aux données (" + ex.ToString + ")")
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End Try
             ' Is er een artikel opgehaald?
            If unArticle Is Nothing Then
                 ' het artikel bestaat niet
                erreurs.Add("L'article d'id=" + id.ToString + " n'existe pas")
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End If
             ' het artikel is gevonden – het wordt aan de huidige sessie toegevoegd
            Session.Item("article") = unArticle
             ' de weergave ervan wordt voorbereid
            context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
            context.Items("msg") = ""
            context.Items("qte") = ""
            Server.Transfer(CType(Application.Item("urlInfos"), String))
        End Sub

Opmerkingen:

  • eventuele fouten worden in een [ArrayList] geplaatst – regel 4
  • de ID van het opgevraagde artikel wordt uit de aanvraag gehaald – regel 6
  • deze identificatiecode wordt gecontroleerd. Deze moet aanwezig zijn en het moet een geheel getal zijn. Als dit niet het geval is, wordt de weergave [ERREURS] verzonden met de bijbehorende foutmelding – regels 7-27
  • zodra de ID is gecontroleerd, wordt het artikel opgevraagd bij het singleton [articlesDomain]. Als er een uitzondering optreedt, wordt de weergave [ERREURS] verzonden - regels 29-39
  • als het artikel niet is gevonden, wordt de weergave [ERREURS] verzonden – regels 41-48
  • als het artikel is gevonden, wordt het in de sessie van de gebruiker geplaatst en vervolgens weergegeven in de weergave [INFOS] – regels 50-56

1.5.7.6. Verwerking van de actie [achat]

De klant heeft het artikel gekocht dat wordt weergegeven in de weergave [INFOS].

Image

De code is als volgt:

         ' aankoop van een artikel
        Public Sub doAchat()
             ' aankoop van een artikel
             ' het artikel dat in de sessie zat, wordt opgehaald
            Dim unArticle As Article = CType(Session.Item("article"), Article)
             ' hebben we iets?
            If unArticle Is Nothing Then
                 ' niet normaal – de lijst met artikelen wordt weergegeven
                doListe()
            End If
             ' de geplaatste hoeveelheid wordt opgehaald
            Dim strQte As String = Request.Form("txtQte")
             ' hebben we iets?
            If strQte Is Nothing Then
                 ' niet normaal – de artikellijst wordt verzonden
                doListe()
            End If
             ' is er een geheel getal?
            Dim qte As Integer
            Try
                qte = Integer.Parse(strQte)
                If (qte <= 0) Then Throw New Exception
            Catch ex As Exception
                 ' niet normaal – we sturen de informatiepagina met het foutbericht
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                context.Items("msg") = "Quantité incorrecte"
                context.Items("qte") = strQte
                Server.Transfer(CType(Application.Item("urlInfos"), String))
            End Try
             ' alles in orde - we plaatsen het artikel in het winkelmandje van de klant
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
            unPanier.ajouter(New Achat(unArticle, qte))
             ' de lijst met artikelen wordt weergegeven
            doListe()
        End Sub

Opmerkingen:

  • het artikel dat in de sessie is opgeslagen, wordt opgehaald – regel 5
  • als het er niet is (de sessie is mogelijk verlopen), wordt de weergave [LISTE] weergegeven – regels 7-10
  • de gekochte hoeveelheid wordt uit de query opgehaald – regel 12
  • de geldigheid ervan wordt gecontroleerd – regels 13-29
  • in geval van ongeldigheid wordt, afhankelijk van de situatie, de weergave [LISTE] verzonden – regel 16 of de weergave [INFOS] – regels 24-28
  • als alles in orde is, wordt de aankoop in het winkelmandje opgeslagen – regels 31-32
  • vervolgens wordt het scherm [LISTE] verzonden – regel 34

1.5.7.7. Verwerking van de actie [panier]

De klant heeft verschillende aankopen gedaan en vraagt om het winkelmandje te bekijken:

Image

De code is als volgt:

         ' winkelwagen bekijken
        Public Sub doPanier()
             'het winkelmandje wordt opgehaald uit de sessie
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
             ' Winkelmandje leeg?
            If unPanier.achats.Count = 0 Then
                 ' weergave van een leeg winkelmandje
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlPanierVide"), String))
            End If
             ' weergave van een niet-leeg winkelmandje
            context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable), CType(options("validationpanier"), Hashtable)}
            Server.Transfer(CType(Application.Item("urlPanier"), String))
        End Sub

Opmerkingen:

  • het winkelmandje wordt opgehaald uit de sessie – regel 4. Er wordt hier niet gecontroleerd of er daadwerkelijk iets wordt opgehaald. Dit zou wel moeten gebeuren, omdat de sessie mogelijk is verlopen.
  • Als het winkelmandje leeg is, wordt de weergave [PANIERVIDE] verzonden – regels 6-10
  • anders wordt de weergave [PANIER] verzonden – regels 11-14

1.5.7.8. Verwerking van de actie [retirerachat]

De klant wil een aankoop uit zijn winkelmandje verwijderen:

Image

De code is als volgt:

         ' een aankoop verwijderen
        Public Sub doRetirerAchat()
             'het winkelmandje uit de sessie ophalen
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
             ' het artikel wordt verwijderd
            Try
                 ' de id van het verwijderde artikel ophalen
                Dim idArticle As Integer = Integer.Parse(Request.QueryString("id"))
                 ' het wordt uit het winkelmandje verwijderd
                unPanier.enlever(idArticle)
            Catch ex As Exception
                 ' de lijst met artikelen wordt weergegeven
                doListe()
            End Try
             ' het winkelmandje weergeven
            doPanier()
        End Sub

Opmerkingen:

  • het winkelmandje wordt opgehaald uit de sessie – regel 4. Er wordt hier niet gecontroleerd of er daadwerkelijk iets wordt opgehaald. Dit zou wel moeten gebeuren, omdat de sessie mogelijk is verlopen.
  • In de query wordt de ID [id] van het te verwijderen artikel opgehaald – regel 8.
  • De bijbehorende aankoop wordt uit het winkelmandje verwijderd – regel 10
  • De geldigheid van de ID van het gekochte artikel is hier niet gecontroleerd. Als deze een ongeldig type heeft, treedt er een uitzondering op die wordt afgehandeld in de regels 11-13. Als deze geldig is maar niet bestaat, doet de methode [panier.enlever] – regel 10 – niets.
  • het nieuwe winkelmandje wordt weergegeven – regel 16

1.5.7.9. Verwerking van de actie [validerpanier]

De klant wil zijn winkelmandje bevestigen:

Image

De code is als volgt:

         ' winkelwagen afrekenen
        Public Sub doValidationPanier()
             ' in eerste instantie geen fouten
            Dim erreurs As ArrayList
             'het winkelmandje wordt opgehaald uit de sessie
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
             ' we proberen het winkelmandje te bevestigen
            Try
                articlesDomain.acheter(unPanier)
                 ' eventuele fouten bij het afrekenen worden genoteerd
                erreurs = articlesDomain.erreurs
            Catch ex As Exception
                 ' de fout wordt genoteerd
                erreurs = New ArrayList
                erreurs.Add(String.Format("Erreur lors de la validation du panier [{0}]", ex.Message))
            End Try
             ' indien er fouten zijn, dan naar de foutpagina
            If erreurs.Count <> 0 Then
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable), CType(options("panier"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End If
             ' alles is in orde – de artikelenlijst wordt weergegeven met een bericht dat de transactie is geslaagd
            context.Items("message") = "Votre panier a été validé"
            doListe()
        End Sub

Opmerkingen:

  • het winkelmandje wordt opgehaald uit de sessie – regel 6. Er wordt hier niet gecontroleerd of er daadwerkelijk iets wordt opgehaald. Dit zou wel moeten gebeuren, omdat de sessie mogelijk is verlopen.
  • Als de sessie is verlopen, hebben we een pointer [nothing] voor het winkelmandje en zal de methode [acheter] – regel 9 – een uitzondering genereren en wordt de weergave [ERREURS] verzonden. Alleen zal de foutmelding voor de gebruiker weinig duidelijk zijn.
  • In de regels 8-16 wordt geprobeerd het uit de sessie opgehaalde winkelmandje te valideren. Sommige aankopen kunnen worden afgewezen als de gevraagde hoeveelheid de voorraad van het betreffende artikel overschrijdt. Deze gevallen worden door de methode [acheter] opgeslagen in een foutenlijst die in regel 11 wordt opgehaald.
  • Als er fouten zijn, wordt de weergave [ERREURS] verzonden – regels 18-23
  • anders wordt de weergave [LISTE] verzonden – regels 25-26

1.6. Conclusion

We hebben hier een applicatie ontwikkeld volgens een MVC-model. Er lijken (april 2005) geen professionele ontwikkelingsframeworks voor MVC en ASP.NET te bestaan, zoals die wel in Java bestaan (Struts, Spring, ...). Het [Spring.net]-project zou er binnenkort een moeten aanbieden. In afwachting daarvan maakt de hierboven beschreven methode een haalbare MVC-ontwikkeling mogelijk voor middelgrote applicaties.