1. Teil 1
Die PDF-Datei des Dokuments ist verfügbar |HIER|.
Die Beispiele des Dokuments sind verfügbar |HIER|.
1.1. Introduction
Ziele des Artikels:
- eine dreischichtige Webanwendung schreiben [interface utilisateur, métier, accès aux données]
- die Anwendung mit Spring konfigurieren IOC
- verschiedene Versionen zu erstellen, indem die Implementierung einer der drei Schichten geändert wird.
Verwendete Tools:
- Visual Studio.net für die Entwicklung – siehe Anhang, Abschnitt 3.1;
- Cassini-Webserver für die Ausführung – siehe Anhang, Abschnitt 3.2;
- Nunit für Unit-Tests – siehe Anhang, Abschnitt 3.4;
- Spring für die Integration und Konfiguration der Schichten der Webanwendung – siehe Anhang, Abschnitt 3.3;
Auf einer Skala von Anfänger über Fortgeschrittene bis hin zu Experten befindet sich dieses Dokument im Bereich [intermédiaire-avancé]. Zum Verständnis sind verschiedene Voraussetzungen erforderlich. Einige davon können in Dokumenten erworben werden, die ich verfasst habe. In diesem Fall verweise ich darauf. Es versteht sich von selbst, dass dies nur ein Vorschlag ist und der Leser seine bevorzugten Dokumente verwenden kann.
- Sprache VB.net: [Einführung in VB.NET anhand von Beispielen (2004)];
- Webprogrammierung in VB.net: [Webentwicklung mit ASP.NET 1.1 (2004)];
- Verwendung des IoC-Aspekts von Spring: [Spring IoC für .NET (2005)];
- Dokumentation zu Spring.net: [Spring.NET | Homepage ]
Dieses Dokument folgt dem roten Faden eines für Java verfassten Dokuments ([Architectures à 3 couches et architectures MVC avec Struts, Spring et Java ]). Wir erstellen in VB.NET die in Java geschriebene dreischichtige Webanwendung MVC. Die Idee, die wir hier vermitteln möchten, ist, dass die Entwicklungsplattformen Java und .NET einander so ähnlich sind, dass die in einem dieser beiden Bereiche erworbenen Kenntnisse im anderen wiederverwendet werden können.
Es scheint keine weithin anerkannte MVC-ASP.NET-Entwicklungslösung zu geben. Die folgende Lösung greift die im Dokument [Webentwicklung mit ASP.NET 1.1 (2004)] vorgestellte Methode auf. Diese hat zwar den Vorteil, dass sie in der J2EE-Entwicklung verbreitete Konzepte nutzt, sollte jedoch nur als das betrachtet werden, was sie ist: c.a.d – eine von vielen Methoden der MVC-Entwicklung. Sobald sich eine Entwicklungsmethode von MVC zu ASP.NET allgemein durchgesetzt hat, sollte diese übernommen werden. Die derzeit in Entwicklung befindliche Version .NET von Spring könnte durchaus eine erste Lösung sein.
1.2. Die Webanwendung „webarticles“
Wir stellen hier die Elemente einer vereinfachten E-Commerce-Webanwendung vor. Diese ermöglicht es Webkunden:
- eine Liste von Artikeln aus einer Datenbank einzusehen
- bestimmte Artikel in einen elektronischen Warenkorb zu legen
- diesen zu bestätigen. Diese Bestätigung hat lediglich zur Folge, dass die Bestände der gekauften Artikel in der Datenbank aktualisiert werden.
Dem Benutzer werden folgende Ansichten angezeigt:
- die Ansicht „LISTE“, die eine Liste der zum Verkauf stehenden Artikel anzeigt ![]() | - die Ansicht „[INFOS]“, die zusätzliche Informationen zu einem Produkt liefert: ![]() |
- die Ansichten „[PANIER]“ und „[PANIERVIDE]“, die den Inhalt des Warenkorbs des Kunden anzeigen
![]() | ![]() |
- die Ansicht [ERREURS], die etwaige Fehler der Anwendung meldet

1.3. Allgemeine Architektur der Anwendung
Wir möchten eine Anwendung mit der folgenden dreischichtigen Struktur erstellen:
![]() |
- Die drei Schichten werden durch die Verwendung von Schnittstellen voneinander unabhängig gemacht
- Die Integration der verschiedenen Schichten erfolgt über Spring
- Jede Schicht verfügt über separate Namespaces: Web (Schicht UI), Domain (Geschäftslogik-Schicht) und DAO (Datenzugriffsschicht).
Die Anwendung folgt einer MVC-Architektur (Model-View-Controller). Wenn wir das obige Schichtschema heranziehen, fügt sich die Architektur wie folgt ein:
![]() |
Die Bearbeitung einer Anfrage eines Kunden erfolgt in folgenden Schritten:
- Der Kunde sendet eine Anfrage an den Controller. Dieser Controller ist hier eine .aspx-Seite, der eine besondere Rolle zukommt. Sie empfängt alle Anfragen der Kunden. Sie ist das Eingangstor der Anwendung. Sie entspricht dem C in MVC.
- Der Controller verarbeitet diese Anfrage. Dazu benötigt er möglicherweise die Unterstützung der Geschäftslogikschicht, die in der Struktur MVC als Modell M bezeichnet wird.
- Der Controller erhält eine Antwort von der Geschäftslogikschicht. Die Anfrage des Kunden wurde bearbeitet. Dies kann zu mehreren möglichen Antworten führen. Ein klassisches Beispiel ist
- eine Fehlerseite, falls die Anfrage nicht korrekt verarbeitet werden konnte
- ansonsten eine Bestätigungsseite
- Der Controller wählt die Antwort (= Ansicht) aus, die an den Client gesendet werden soll. Dabei handelt es sich meist um eine Seite mit dynamischen Elementen. Der Controller stellt diese der Ansicht zur Verfügung.
- Die Ansicht wird an den Client gesendet. Dies ist das „V“ in MVC.
1.4. Das Modell
Wir befassen uns hier mit dem M von MVC. Das Modell besteht hier aus folgenden Elementen:
- die Geschäftsklassen
- die Datenzugriffsklassen
- die Datenbank
1.4.1. Die Datenbank
Die Datenbank enthält nur eine Tabelle namens ARTICLES. Diese wurde mit den folgenden Befehlen SQL generiert:
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
);
/* Einschränkungen */
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);
/* Primärschlüssel */
ALTER TABLE ARTICLES ADD CONSTRAINT PK_ARTICLES PRIMARY KEY (ID);
Primärschlüssel, der einen Artikel eindeutig identifiziert | |
Name des Artikels | |
sein Preis | |
Aktueller Lagerbestand | |
der Lagerbestand, bei dessen Unterschreitung eine Nachbestellung erfolgen muss |
1.4.2. Die Namensräume des Modells
Das Modell M wird hier in Form von zwei Namensräumen bereitgestellt:
- istia.st.articles.dao: enthält die Klassen für den Datenzugriff der Schicht [dao]
- istia.st.articles.domain: enthält die Fachklassen der Schicht [domain]
Jeder dieser Namespaces wird in einer eigenen „Assembly“-Datei generiert:
contenu | rôle | |
- [IArticlesDao]: die Schnittstelle für den Zugriff auf die Ebene [dao]. Dies ist die einzige Schnittstelle, die die Schicht [domain] sieht. Sie sieht keine andere. - [Article]: Klasse zur Definition eines Artikels - [ArticlesDaoArrayList]: Implementierungsklasse der der Schnittstelle [IArticlesDao] mit einer Klasse [ArrayList] | Datenzugriffsschicht - befindet sich vollständig in der [dao] der 3-Tier-Architektur der der Webanwendung | |
- [IArticlesDomain]: die Schnittstelle für den Zugriff auf die Schicht [domain]. Dies ist die einzige Schnittstelle, die die Webschicht sieht. Sie sieht keine andere. - [AchatsArticles]: eine Klasse, die [IArticlesDomain] implementiert - [Achat]: Klasse, die den Einkauf eines Kunden repräsentiert - [Panier]: Klasse, die die Gesamtheit der Einkäufe eines Kunden darstellt | stellt das Modell der Einkäufe im Web – befindet sich vollständig in der Schicht [domain] der Architektur 3-Tier-Architektur der Webanwendung |
1.4.3. Die Schicht [dao]
Die Schicht [dao] enthält folgende Elemente:
-
[IArticlesDao]: Schnittstelle für den Zugriff auf die Schicht [dao]
-
[Article]: Klasse zur Definition eines Artikels
-
[ArticlesDaoArrayList]: Implementierungsklasse der Schnittstelle [IArticlesDao] mit einer Klasse [ArrayList]
Die Struktur des Projekts [Visual Studio] der Schicht [dao] ist wie folgt:

Anmerkungen:
- Das Projekt [dao] ist vom Typ [bibliothèque de classes]
- Die Klassen wurden in einer Baumstruktur mit dem Stammordner „[istia]“ abgelegt. Sie befinden sich alle im Namensraum „[istia.st.articles.dao]“.
1.4.3.1. Die Klasse [Article]
Die Klasse, die einen Artikel definiert, lautet wie folgt:
Imports System
Namespace istia.st.articles.dao
Public Class Article
' private Felder
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
' Artikelpreis
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
' aktueller Artikelbestand
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
' Mindestbestand des Artikels
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
' Standardhersteller
Public Sub New()
End Sub
' Hersteller mit Eigenschaften
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
' Artikelidentifikationsmethode
Public Overrides Function ToString() As String
Return "[" + id.ToString + "," + nom + "," + prix.ToString + "," + stockactuel.ToString + "," + stockminimum.ToString + "]"
End Function
End Class
End Namespace
Diese Klasse bietet:
- einen Konstruktor, mit dem die 5 Informationen eines Artikels festgelegt werden können: [id, nom, prix, stockactuel, stockminimum]
- öffentliche Eigenschaften zum Lesen und Schreiben der 5 Informationen.
- eine Überprüfung der in den Artikel eingegebenen Daten. Bei fehlerhaften Daten wird eine Ausnahme ausgelöst.
- eine Methode toString, mit der der Wert eines Artikels als Zeichenkette abgerufen werden kann. Dies ist häufig für die Fehlersuche in einer Anwendung nützlich.
1.4.3.2. Die Schnittstelle [IArticlesDao]
Die Schnittstelle [IArticlesDao] ist wie folgt definiert:
Imports System
Imports System.Collections
Namespace istia.st.articles.dao
Public Interface IArticlesDao
' Liste aller Artikel
Function getAllArticles() As IList
' Artikel hinzufügen
Function ajouteArticle(ByVal unArticle As Article) As Integer
' Artikel löschen
Function supprimeArticle(ByVal idArticle As Integer) As Integer
' Artikel bearbeiten
Function modifieArticle(ByVal unArticle As Article) As Integer
' Artikel suchen
Function getArticleById(ByVal idArticle As Integer) As Article
' Löscht alle Artikel
Sub clearAllArticles()
' ändert den Lagerbestand eines Artikels
Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
End Interface
End Namespace
Die verschiedenen Methoden der Schnittstelle haben folgende Funktionen:
gibt alle Artikel aus der Datenquelle zurück | |
leert die Datenquelle | |
gibt das durch seinen Primärschlüssel identifizierte Objekt [Article] zurück | |
ermöglicht das Hinzufügen eines Artikels zur Datenquelle | |
ermöglicht die Änderung eines Artikels in der Datenquelle | |
ermöglicht das Löschen eines Artikels aus der Datenquelle | |
ermöglicht die Änderung des Bestands eines Artikels in der Datenquelle |
Die Schnittstelle stellt Client-Programmen eine Reihe von Methoden zur Verfügung, die ausschließlich durch ihre Signaturen definiert sind. Sie kümmert sich nicht darum, wie diese Methoden tatsächlich implementiert werden. Dies sorgt für Flexibilität in einer Anwendung. Das Client-Programm ruft die Schnittstelle auf und nicht eine bestimmte Implementierung davon.
![]() |
Die Auswahl einer bestimmten Implementierung erfolgt über eine Spring-Konfigurationsdatei. Um zu zeigen, dass zum Testen der Webanwendung ausschließlich die Schnittstelle für den Datenzugriff von Bedeutung ist und nicht deren Implementierungsklasse, werden wir die Datenquelle zunächst durch ein einfaches Objekt [ArrayList] implementieren. Zu einem späteren Zeitpunkt werden wir eine Lösung auf Basis von SGBD vorstellen.
1.4.3.3. Die Implementierungsklasse [ArticlesDaoArrayList]
Die Implementierungsklasse [ArticlesDaoArrayList] ist wie folgt definiert:
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
' Standardhersteller
Public Sub New()
' Es werden einige Artikel erstellt
For i As Integer = 1 To nbArticles
articles.Add(New Article(i, "article" + i.ToString, i * 10, i * 10, i * 10))
Next
End Sub
' Liste aller Artikel
Public Function getAllArticles() As IList Implements IArticlesDao.getAllArticles
' Die Liste der Artikel wird zurückgegeben
SyncLock Me
Return articles
End SyncLock
End Function
' Alle Artikel löschen
Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles
' Die Artikelliste wird geleert
SyncLock Me
articles.Clear()
End SyncLock
End Sub
' einen Artikel anhand seines Schlüssels abrufen
Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById
' Artikel in der Liste suchen
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
' einen Artikel zur Artikelliste hinzufügen
Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle
' Der Artikel wird zur Artikelliste hinzugefügt
SyncLock Me
' Es wird überprüft, ob er nicht bereits vorhanden ist
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
' Der Artikel wird hinzugefügt
articles.Add(unArticle)
' das Ergebnis wird zurückgegeben
Return 1
End SyncLock
End Function
' einen Artikel bearbeiten
Public Function modifieArticle(ByVal articleNouveau As Article) As Integer Implements IArticlesDao.modifieArticle
' einen Artikel bearbeiten
SyncLock Me
' Es wird geprüft, ob er existiert
Dim ipos As Integer = posArticle(articles, articleNouveau.id)
' falls er nicht existiert
If ipos = -1 Then Return 0
' er existiert – man bearbeitet ihn
articles(ipos) = articleNouveau
' das Ergebnis wird zurückgegeben
Return 1
End SyncLock
End Function
' einen durch seinen Schlüssel identifizierten Artikel löschen
Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle
' Löschen eines Artikels
SyncLock Me
' Es wird geprüft, ob er vorhanden ist
Dim ipos As Integer = posArticle(articles, idArticle)
' Falls er nicht existiert
If ipos = -1 Then Return 0
' er existiert – er wird gelöscht
articles.RemoveAt(ipos)
' das Ergebnis wird zurückgegeben
Return 1
End SyncLock
End Function
' den Bestand eines Artikels ändern, der durch seinen Schlüssel identifiziert wird
Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle
' Bestand eines Artikels ändern
SyncLock Me
' Es wird geprüft, ob er vorhanden ist
Dim ipos As Integer = posArticle(articles, idArticle)
' falls er nicht vorhanden ist
If ipos = -1 Then Return 0
' Er existiert – sein Bestand wird geändert, sofern dies möglich ist
Dim unArticle As Article = CType(articles(ipos), Article)
' Der Bestand wird nur geändert, wenn er ausreichend ist
If unArticle.stockactuel + mouvement >= 0 Then
unArticle.stockactuel += mouvement
Return 1
Else
Return 0
End If
End SyncLock
End Function
' einen Artikel anhand seines Schlüssels suchen
Private Function posArticle(ByVal listArticles As ArrayList, ByVal idArticle As Integer) As Integer
' gibt die Position des Artikels [idArticle] in der Liste zurück oder -1, falls er nicht gefunden wurde
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
' nicht gefunden
Return -1
End Function
End Class
End Namespace
Anmerkungen:
- Die Datenquelle wird durch das private Feld [articles] vom Typ [ArrayList] simuliert
- Der Konstruktor der Klasse legt standardmäßig 4 Einträge in der Datenquelle an.
- Alle Methoden für den Datenzugriff wurden synchronisiert, um Probleme durch gleichzeitigen Zugriff auf die Datenquelle zu vermeiden. Zu einem bestimmten Zeitpunkt hat jeweils nur ein Thread Zugriff auf eine bestimmte Methode.
- Die Methode [posArticle] ermöglicht es, die Position [0..N] eines Artikels, der durch seine Nummer identifiziert wird, in der Quelle [ArrayList] zu ermitteln. Existiert der Artikel nicht, gibt die Methode die Position -1 zurück. Diese Methode wird wiederholt von den anderen Methoden aufgerufen.
- Die Methode [ajouteArticle] ermöglicht es, einen Artikel zur Artikelliste hinzuzufügen. Sie gibt die Anzahl der eingefügten Artikel zurück: 1. Wenn der Artikel bereits existierte, wird eine Ausnahme ausgelöst.
- Die Methode [modifieArticle] ermöglicht es, einen vorhandenen Artikel zu ändern. Sie gibt die Anzahl der geänderten Artikel zurück: 1, wenn der Artikel vorhanden war, andernfalls 0.
- Die Methode [supprimeArticle] ermöglicht das Löschen eines vorhandenen Artikels. Sie gibt die Anzahl der gelöschten Artikel zurück: 1, wenn der Artikel vorhanden war, andernfalls 0.
- Die Methode [getAllArticles] gibt die Liste aller Artikel zurück.
- Die Methode [getArticleById] ermöglicht es, einen Artikel anhand seiner Nummer abzurufen. Der Wert [nothing] wird zurückgegeben, wenn der Artikel nicht existiert.
- Der Code stellt keine wirkliche Schwierigkeit dar. Wir überlassen es dem Leser, ihn durchzugehen und zu verstehen.
1.4.3.4. Erstellung der Assembly der Schicht [dao]
Das Visual Studio-Projekt ist so konfiguriert, dass die Assembly [webarticles-dao.dll] generiert wird. Diese wird im Ordner [bin] des Projekts generiert:
![]() | ![]() |
1.4.3.5. NUnit-Tests der Schicht [dao]
In Java werden die Klassen mit dem Framework [Junit] getestet. In .NET bietet das Nunit-Framework dieselben Möglichkeiten für Unit-Tests:

Die Struktur des Visual Studio-Testprojekts sieht wie folgt aus:

Anmerkungen:
- Das Projekt [tests] ist vom Typ [bibliothèque de classes]
- Die Tests [NUnit] erfordern einen Verweis auf die Assembly [nunit.framework.dll]
- Die Testklasse [NUnit] ruft über Spring eine Instanz des zu testenden Objekts ab. Daher befinden sich
- im Ordner [bin] die Spring-Klassendateien
- in [References] einen Verweis auf die Assembly [Spring-Core.dll] aus dem Ordner [bin]
- in [bin] eine Konfigurationsdatei für Spring
- Die Testklasse benötigt die Assembly [webarticles-dao.dll] aus der Ebene [dao]. Diese wurde im Ordner [bin] abgelegt und ihr Verweis zu den Projektverweisen hinzugefügt.
Eine Testklasse [NUnit] benötigt Zugriff auf die Klassen des Namensraums [NUnit.Framework]. Daher enthält sie die folgende Importanweisung:
Der Namensraum [NUnit.Framework] befindet sich in der „Assembly“ [nunit.framework.dll], die zu den Projektverweisen hinzugefügt werden muss:
![]() | ![]() |
Die Assembly [nunit.framework.dll] sollte in der vorgeschlagenen Liste enthalten sein, wenn die Installation von [Nunit] bereits erfolgt ist. Ein Doppelklick auf die Assembly reicht aus, um sie dem Projekt hinzuzufügen:

Die Testklasse [NUnit] der Schicht [dao] könnte wie folgt aussehen:
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
' das zu testende Objekt
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' Es wird eine Instanz des Spring-Objektgenerators abgerufen
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open))
' die Instanziierung des Objekts „articlesdao“ wird angefordert
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
End Sub
<Test()> _
Public Sub testGetAllArticles()
' Sichtprüfung
listArticles()
End Sub
<Test()> _
Public Sub testClearAllArticles()
' Alle Artikel werden gelöscht
articlesDao.clearAllArticles()
' Alle Artikel werden abgefragt
Dim articles As IList = articlesDao.getAllArticles
' Überprüfung: Es müssen 0 vorhanden sein
Assert.AreEqual(0, articles.Count)
End Sub
<Test()> _
Public Sub testAjouteArticle()
' Alle Artikel löschen
articlesDao.clearAllArticles()
' Überprüfung: Die Artikeltabelle muss leer sein
Dim articles As IList = articlesDao.getAllArticles
Assert.AreEqual(0, articles.Count)
' Es werden zwei Artikel hinzugefügt
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' Überprüfung: Es müssen zwei Artikel vorhanden sein
articles = articlesDao.getAllArticles
Assert.AreEqual(2, articles.Count)
' Sichtprüfung
listArticles()
End Sub
<Test()> _
Public Sub testSupprimeArticle()
' Alle Artikel werden gelöscht
articlesDao.clearAllArticles()
' Überprüfung: Die Artikeltabelle muss leer sein
Dim articles As IList = articlesDao.getAllArticles
Assert.AreEqual(0, articles.Count)
' Es werden zwei Artikel hinzugefügt
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' Überprüfung: Es müssen 2 Artikel vorhanden sein
articles = articlesDao.getAllArticles
Assert.AreEqual(2, articles.Count)
' Artikel 4 wird gelöscht
articlesDao.supprimeArticle(4)
' Überprüfung: Es muss 1 Artikel übrig bleiben
articles = articlesDao.getAllArticles
Assert.AreEqual(1, articles.Count)
' Sichtprüfung
listArticles()
End Sub
<Test()> _
Public Sub testModifieArticle()
' Alle Artikel löschen
articlesDao.clearAllArticles()
' Überprüfung
Dim articles As IList = articlesDao.getAllArticles
Assert.AreEqual(0, articles.Count)
' Hinzufügen von 2 Artikeln
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' Überprüfung
articles = articlesDao.getAllArticles
Assert.AreEqual(2, articles.Count)
' Suche nach Artikel 3
Dim unArticle As Article = articlesDao.getArticleById(3)
' Überprüfung
Assert.AreEqual(unArticle.nom, "article3")
' Suche nach Artikel 4
unArticle = articlesDao.getArticleById(4)
' Überprüfung
Assert.AreEqual(unArticle.nom, "article4")
' Änderung von Artikel 4
articlesDao.modifieArticle(New Article(4, "article4", 44, 44, 44))
' Überprüfung
unArticle = articlesDao.getArticleById(4)
Assert.AreEqual(unArticle.prix, 44, 0.000001)
' Sichtprüfung
listArticles()
End Sub
<Test()> _
Public Sub testGetArticleById()
' Artikel löschen
articlesDao.clearAllArticles()
' Überprüfung
Dim articles As IList = articlesDao.getAllArticles
Assert.AreEqual(0, articles.Count)
' Hinzufügen von 2 Artikeln
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' Überprüfung
articles = articlesDao.getAllArticles
Assert.AreEqual(2, articles.Count)
' Suche nach Artikel 3
Dim unArticle As Article = articlesDao.getArticleById(3)
' Überprüfung
Assert.AreEqual(unArticle.nom, "article3")
' Suche nach Artikel 4
unArticle = articlesDao.getArticleById(4)
' Überprüfung
Assert.AreEqual(unArticle.nom, "article4")
End Sub
' Bildschirmauflistung
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()
' Löschen aller Artikel
articlesDao.clearAllArticles()
' Artikelsuche 1
Dim article As article = articlesDao.getArticleById(1)
' Überprüfung
Assert.IsNull(article)
' Änderung eines nicht vorhandenen Artikels
Dim i As Integer = articlesDao.modifieArticle(New article(1, "1", 1, 1, 1))
' Es durften keine Zeilen geändert werden
Assert.AreEqual(i, 0)
' Löschen eines nicht vorhandenen Artikels
i = articlesDao.supprimeArticle(1)
' Es mussten keine Zeilen gelöscht werden
Assert.AreEqual(0, i)
End Sub
<Test()> _
Public Sub testChangerStockArticle()
' Löschen aller Artikel
articlesDao.clearAllArticles()
' Hinzufügen eines Artikels
Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3))
Assert.AreEqual(nbArticles, 1)
' Hinzufügen eines Artikels
nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
Assert.AreEqual(nbArticles, 1)
' Erstellung von 100 Threads
Dim taches(99) As Thread
For i As Integer = 0 To taches.Length - 1
' Der Thread i wird erstellt
taches(i) = New Thread(New ThreadStart(AddressOf décrémente))
' Der Name des Threads wird festgelegt
taches(i).Name = "tache_" & i
' Der Thread i wird gestartet
taches(i).Start()
Next
' Es wird auf das Ende aller Threads gewartet
For i As Integer = 0 To taches.Length - 1
taches(i).Join()
Next
' Prüfungen – Artikel 3 muss einen Bestand von 1 haben
Dim unArticle As Article = articlesDao.getArticleById(3)
Assert.AreEqual(unArticle.nom, "article3")
Assert.AreEqual(1, unArticle.stockactuel)
' Der Lagerbestand von Artikel 4 wird verringert
Dim erreur As Boolean = False
Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100)
' Prüfung: Der Bestand sollte sich nicht verändert haben
Assert.AreEqual(0, nbLignes)
' Sichtprüfung
listArticles()
End Sub
Public Sub décrémente()
' Thread gestartet
System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lancé")
' Thread verringert den Lagerbestand
articlesDao.changerStockArticle(3, -1)
' Thread beendet
System.Console.Out.WriteLine(Thread.CurrentThread.Name + " terminé")
End Sub
End Class
End Namespace
Anmerkungen:
- Wir wollten ein Testprogramm für die Schnittstelle [IArticlesDao] schreiben, das unabhängig von der Implementierungsklasse dieser Schnittstelle ist. Daher haben wir Spring verwendet, um den Namen der Implementierungsklasse vor dem Testprogramm zu verbergen.
- Die Attributmethode <Setup()> ruft von Spring eine Referenz auf das zu testende Objekt [articlesdao] ab. Dieses ist in der folgenden Datei [spring-config.xml] definiert:
<?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>
Diese Datei gibt den Namen der Implementierungsklasse [istia.st.articles.dao.ArticlesDaoArrayList] der Schnittstelle [IArticlesDao] an und wo sie zu finden ist: [ webarticles-dao.dll]. Da die Instanziierung keine Parameter erfordert, sind hier keine definiert.
- Die meisten Tests sind leicht verständlich. Der Leser wird gebeten, die Kommentare zu lesen.
- Die Methode [testChangerStockArticle] bedarf einiger Erläuterungen. Sie erstellt 100 Threads, die den Lagerbestand eines bestimmten Artikels verringern sollen.
<Test()> _
Public Sub testChangerStockArticle()
' Alle Artikel löschen
articlesDao.clearAllArticles()
' Artikel hinzufügen
Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3))
Assert.AreEqual(nbArticles, 1)
' Artikel hinzufügen
nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
Assert.AreEqual(nbArticles, 1)
' Erstellung von 100 Threads
Dim taches(99) As Thread
For i As Integer = 0 To taches.Length - 1
' Der Thread i wird erstellt
taches(i) = New Thread(New ThreadStart(AddressOf décrémente))
' Der Name des Threads wird festgelegt
taches(i).Name = "tache_" & i
' Start der Ausführung von Thread i
taches(i).Start()
Next
' Warten auf das Ende aller Threads
For i As Integer = 0 To taches.Length - 1
taches(i).Join()
Next
' Prüfungen – Artikel 3 muss einen Bestand von 1 haben
Dim unArticle As Article = articlesDao.getArticleById(3)
Assert.AreEqual(unArticle.nom, "article3")
Assert.AreEqual(1, unArticle.stockactuel)
' Der Lagerbestand von Artikel 4 wird verringert
Dim erreur As Boolean = False
Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100)
' Prüfung: Der Bestand sollte sich nicht verändert haben
Assert.AreEqual(0, nbLignes)
' Sichtprüfung
listArticles()
End Sub
Hier geht es darum, gleichzeitige Zugriffe auf die Datenquelle zu testen. Die Methode zur Aktualisierung des Lagerbestands lautet wie folgt:
Public Sub décrémente()
' Thread gestartet
System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lancé")
' Thread verringert den Lagerbestand
articlesDao.changerStockArticle(3, -1)
' Thread beendet
System.Console.Out.WriteLine(Thread.CurrentThread.Name + " terminé")
End Sub
Sie verringert den Bestand von Artikel Nr. 3 um eine Einheit. Wenn man sich den Code der Methode [testChangerStockArticle] ansieht, stellt man fest, dass:
- der Bestand des Artikels Nr. 3 auf 101 initialisiert wird
- die 100 Threads verringern diesen Bestand jeweils um eine Einheit
- Am Ende der Ausführung aller Threads sollte der Bestand daher bei 1 liegen
Außerdem wird in derselben Methode versucht, den Bestand von Artikel Nr. 4 auf einen negativen Wert zu setzen. Dies muss fehlschlagen.
Um die Schicht [dao] zu testen, generieren wir die Dateien DLL und [tests-webarticles-dao.dll] im Ordner [bin] des Projekts [tests]:
![]() | ![]() |
Anschließend laden wir mithilfe der Anwendung [Nunit-Gui] diese DLL und führen die Tests durch:

Im linken Fenster sehen wir die Liste der getesteten Methoden. Die Farbe des Punktes vor dem Namen jeder Methode zeigt an, ob die Methode erfolgreich (grün) oder fehlgeschlagen (rot) ist. Der Leser, der dieses Dokument auf dem Bildschirm betrachtet, kann sehen, dass alle Tests erfolgreich waren. Im weiteren Verlauf gehen wir davon aus, dass wir über eine funktionsfähige Schicht [dao] verfügen.
1.4.4. Die Schicht [domain]
Die Schicht [domain] enthält folgende Elemente:
-
[IArticlesDomain]: die Schnittstelle für den Zugriff auf die Schicht [domain]
-
[Achat]: Klasse, die einen Einkauf definiert
-
[Panier]: Klasse, die einen Warenkorb definiert
-
[AchatsArticles]: Implementierungsklasse der Schnittstelle [IArticlesDomain]
Die Struktur der Lösung [Visual Studio] der Schicht [domain] ist wie folgt:

Anmerkungen:
- Das Projekt [domain] ist vom Typ [bibliothèque de classes]
- Die Klassen wurden in einer Baumstruktur mit dem Stammordner „[istia]“ abgelegt. Sie befinden sich alle im Namensraum „[istia.st.articles.domain]“.
- Die Klasse DLL aus der Ebene [dao] wurde in den Ordner [bin] des neuen Projekts verschoben. Außerdem wurde diese Klasse DLL als Referenz zum Projekt hinzugefügt.
1.4.4.1. Die Schnittstelle [IArticlesDomain]
Die Schnittstelle [IArticlesDomain] entkoppelt die Schicht [métier] von der Schicht [web]. Letztere greift über diese Schnittstelle auf die Schicht [métier/domain] zu, ohne sich um die Klasse zu kümmern, die sie tatsächlich implementiert. Die Schnittstelle definiert die folgenden Aktionen für den Zugriff auf die Geschäftsschicht:
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
gibt die Objektliste [Article] der zugehörigen Datenquelle zurück | |
gibt das durch [idArticle] identifizierte Objekt [Article] zurück | |
validiert den Warenkorb des Kunden, indem die Bestände der gekauften Artikel um die gekaufte Menge verringert werden – kann fehlschlagen, wenn der Bestand nicht ausreicht | |
gibt die Liste der aufgetretenen Fehler zurück – leer, wenn keine Fehler vorliegen |
1.4.4.2. Die Klasse [Achat]
Die Klasse [Achat] repräsentiert einen Kauf des Kunden:
Imports istia.st.articles.dao
Namespace istia.st.articles.domain
Public Class Achat
' private Felder
Private _article As article
Private _qte As Integer
' Standardkonstruktor
Public Sub New()
End Sub
' Konstruktor mit Parametern
Public Sub New(ByVal unArticle As article, ByVal qte As Integer)
' über Eigenschaften
Me.article = unArticle
Me.qte = qte
End Sub
' gekaufter Artikel
Public Property article() As article
Get
Return _article
End Get
Set(ByVal Value As article)
_article = Value
End Set
End Property
' gekaufte Menge
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
' Gesamtbetrag des Kaufs
Public ReadOnly Property totalAchat() As Double
Get
Return _qte * _article.prix
End Get
End Property
' Identität
Public Overrides Function ToString() As String
Return "[" + _article.ToString + "," + _qte.ToString + "]"
End Function
End Class
End Namespace
Anmerkungen:
- Die Klasse [Achat] verfügt über die folgenden Eigenschaften und Methoden:
Der gekaufte Artikel | |
die gekaufte Menge | |
Kaufbetrag | |
Identitätszeichenfolge des Objekts |
- Sie verfügt über einen Konstruktor, mit dem die Eigenschaften [article, qte] initialisiert werden können, die einen Kauf definieren.
1.4.4.3. Die Klasse [Panier]
Die Klasse [Panier] repräsentiert die Gesamtheit der Einkäufe des Kunden:
Namespace istia.st.articles.domain
Public Class Panier
' Private Felder
Private _achats As New ArrayList
Private _totalPanier As Double = 0
' Standardhersteller
Public Sub New()
End Sub
' Einkaufsliste
Public ReadOnly Property achats() As ArrayList
Get
Return _achats
End Get
End Property
' Gesamtbetrag der Einkäufe
Public ReadOnly Property totalPanier() As Double
Get
Return _totalPanier
End Get
End Property
' Methoden
Public Sub ajouter(ByVal unAchat As Achat)
' Es wird geprüft, ob der Einkauf bereits vorhanden ist
Dim iAchat As Integer = posAchat(unAchat.article.id)
If iAchat <> -1 Then
' Es wurde gefunden
Dim achatCourant As Achat = CType(_achats(iAchat), Achat)
achatCourant.qte += unAchat.qte
Else
' nicht gefunden
_achats.Add(unAchat)
End If
' Die Warenkorb-Gesamtsumme wird erhöht
_totalPanier += unAchat.totalAchat
End Sub
' einen Artikel aus dem Warenkorb entfernen
Public Sub enlever(ByVal idAchat As Integer)
' Nach dem Artikel suchen
Dim iachat As Integer = posAchat(idAchat)
' Wenn gefunden, wird der Artikel entfernt
If iachat <> -1 Then
Dim achatCourant As Achat = CType(_achats(iachat), Achat)
' aus dem Warenkorb entfernen
_achats.RemoveAt(iachat)
' Warenkorb-Gesamtbetrag verringern
_totalPanier -= achatCourant.totalAchat
End If
End Sub
Private Function posAchat(ByVal idArticle As Integer) As Integer
' Sucht einen Kauf in der Kaufliste
' gibt die Position in der Liste zurück oder -1, wenn nicht gefunden
Dim achatCourant As Achat
Dim trouvé As Boolean = False
Dim i As Integer = 0
While Not trouvé AndAlso i < _achats.Count
' aktueller Einkauf
achatCourant = CType(_achats(i), Achat)
' Vergleich mit dem gesuchten Artikel
If achatCourant.article.id = idArticle Then
Return i
End If
'nächster Kauf
i += 1
End While
' nicht gefunden
Return -1
End Function
' Identitätsfunktion
Public Overrides Function ToString() As String
Return _achats.ToString
End Function
End Class
End Namespace
Anmerkungen:
- Die Klasse [Panier] verfügt über die folgenden Eigenschaften und Methoden:
Die Einkaufsliste des Kunden – Liste von Objekten vom Typ [Achat] | |
Fügt einen Einkauf zur Einkaufsliste hinzu | |
Entfernt den Kauf des Artikels idAchat | |
Gesamtbetrag der Einkäufe im Warenkorb | |
gibt die Identitätszeichenfolge des Warenkorbs zurück |
- Die Methode [posAchat] ist eine Hilfsmethode, mit der die Position eines durch die Artikelnummer identifizierten Kaufs in der Einkaufsliste ermittelt werden kann. Die Einkaufsliste wird so verwaltet, dass ein mehrfach gekaufter Artikel nur eine Position in der Liste einnimmt. Somit kann ein Kauf anhand der Artikelnummer identifiziert werden. Die Methode [posAchat] gibt -1 zurück, wenn der gesuchte Kauf nicht vorhanden ist.
- Die Methode [ajouter] fügt der Einkaufsliste einen neuen Einkauf hinzu. Dies bedeutet entweder, dass ein neuer Eintrag zur Einkaufsliste hinzugefügt wird, falls der gekaufte Artikel noch nicht in der Liste vorhanden war, oder dass die gekaufte Menge erhöht wird, falls er bereits vorhanden war.
- Die Methode [enlever] ermöglicht es, einen durch eine Nummer identifizierten Kauf aus der Kaufliste zu entfernen. Wenn der Kauf nicht vorhanden ist, führt die Methode keine Aktion aus.
- Der Gesamtbetrag der Einkäufe ([totalPanier]) wird bei Hinzufügungen und Entfernungen von Einkäufen beibehalten.
1.4.4.4. Die Klasse [AchatsArticles]
Die Schnittstelle [IArticlesDomain] wird durch die folgende Klasse [AchatsArticles] implementiert:
Imports istia.st.articles.dao
Namespace istia.st.articles.domain
Public Class AchatsArticles
Implements IArticlesDomain
'Private Felder
Private _articlesDao As IArticlesDao
Private _erreurs As ArrayList
' Konstruktor
Public Sub New(ByVal articlesDao As IArticlesDao)
_articlesDao = articlesDao
End Sub
' Fehlerliste
Public ReadOnly Property erreurs() As ArrayList Implements IArticlesDomain.erreurs
Get
Return _erreurs
End Get
End Property
' Artikelübersicht
Public Function getAllArticles() As IList Implements IArticlesDomain.getAllArticles
' Liste aller Artikel
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
' einen Artikel anhand seiner Nummer abrufen
Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDomain.getArticleById
' einen bestimmten 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
' Warenkorb kaufen
Public Sub acheter(ByVal panier As Panier) Implements IArticlesDomain.acheter
' Warenkorb kaufen – die Bestände der gekauften Artikel müssen verringert werden
_erreurs = New ArrayList
Dim achat As achat
Dim achats As ArrayList = panier.achats
For i As Integer = achats.Count - 1 To 0 Step -1
' Bestand von Artikel i verringern
achat = CType(achats(i), achat)
Try
If _articlesDao.changerStockArticle(achat.article.id, -achat.qte) = 0 Then
' Der Vorgang konnte nicht ausgeführt werden
_erreurs.Add("L'achat " + achat.ToString + " n'a pu se faire - Vérifiez les stocks")
Else
' Der Vorgang wurde ausgeführt – der Kauf wird aus dem Warenkorb entfernt
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
Anmerkungen:
- Diese Klasse implementiert die vier Methoden der Schnittstelle [IArticlesDomain]. Sie verfügt über zwei private Felder:
das Datenzugriffsobjekt | |
die Liste eventueller Fehler. Sie ist über die öffentliche Eigenschaft [erreurs] zugänglich |
- Um eine Instanz der Klasse zu erstellen, muss das Objekt bereitgestellt werden, das den Zugriff auf die Daten ermöglicht:
- Die Methoden [getAllArticles] und [getArticleById] basieren auf den gleichnamigen Methoden der Schicht [dao]
- Die Methode [acheter] bestätigt den Kauf eines Warenkorbs. Diese Bestätigung besteht lediglich darin, die Bestände der gekauften Artikel zu verringern. Der Kauf eines Artikels ist nur möglich, wenn der Lagerbestand dies zulässt. Ist dies nicht der Fall, wird der Kauf abgelehnt: Der Artikel verbleibt im Warenkorb und in der Liste [erreurs] wird ein Fehler gemeldet. Ein bestätigter Kauf wird aus dem Warenkorb entfernt und der Lagerbestand des entsprechenden Artikels um die gekaufte Menge verringert.
1.4.4.5. Erstellung der Assembly der Schicht [domain]
Das Visual Studio-Projekt ist so konfiguriert, dass die Assembly [webarticles-domain.dll] generiert wird. Diese wird im Ordner [bin] des Projekts generiert:
![]() | ![]() |
1.4.4.6. Tests NUnit der Schicht [domain]
Die Struktur des Visual Studio-Testprojekts sieht wie folgt aus:

Anmerkungen:
- Das Projekt [tests] ist vom Typ [bibliothèque de classes]
- Die Tests [NUnit] erfordern einen Verweis auf die Assembly [nunit.framework.dll]
- Die Testklasse [NUnit] ruft über Spring eine Instanz des zu testenden Objekts ab. Daher befinden sich
- im Ordner „[bin]“ die Spring-Klassendateien
- in [References] einen Verweis auf die Assembly [Spring-Core.dll] aus dem Ordner [bin]
- in [bin] eine Konfigurationsdatei für Spring
- Die Testklasse benötigt die Assembly [webarticles-dao.dll] aus der Ebene [dao] und die Assembly [webarticles-domain.dll] aus der Ebene [domain]. Diese wurden im Ordner „[bin]“ abgelegt und ihre Verweise zu den Projektverweisen hinzugefügt.
Eine Testklasse NUnit der Ebene [domain] könnte wie folgt aussehen:
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
' das zu testende Objekt
Private articlesDomain As IArticlesDomain
Private articlesDao As IArticlesDao
<SetUp()> _
Public Sub init()
' Eine Instanz des Spring-Objektgenerators wird abgerufen
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open))
' Die Instanziierung des DAO-Objekts „articles“ wird angefordert
articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
' anschließend die des „articlesdomain“-Objekts
articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDomain)
End Sub
<Test()> _
Public Sub getAllArticles()
' Sichtprüfung
listArticles()
End Sub
<Test()> _
Public Sub getArticleById()
' Löschen der Artikel
articlesDao.clearAllArticles()
' Überprüfung
Dim articles As IList = articlesDomain.getAllArticles
Assert.AreEqual(0, articles.Count)
' Hinzufügen von 2 Artikeln
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' Überprüfung
articles = articlesDomain.getAllArticles
Assert.AreEqual(2, articles.Count)
' Suche nach Artikel 3
Dim unArticle As Article = articlesDomain.getArticleById(3)
' Überprüfung
Assert.AreEqual(unArticle.nom, "article3")
' Suche nach Artikel 4
unArticle = articlesDao.getArticleById(4)
' Überprüfung
Assert.AreEqual(unArticle.nom, "article4")
End Sub
<Test()> _
Public Sub acheterPanier()
' Artikel löschen
articlesDao.clearAllArticles()
' Überprüfung
Dim articles As IList = articlesDomain.getAllArticles
Assert.AreEqual(0, articles.Count)
' Hinzufügen von 2 Artikeln
articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
' Überprüfung
articles = articlesDomain.getAllArticles
Assert.AreEqual(2, articles.Count)
' Erstellung eines Warenkorbs mit zwei Artikeln
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))
' Überprüfungen
Assert.AreEqual(700, panier.totalPanier, 0.000001)
Assert.AreEqual(2, panier.achats.Count)
' Warenkorb bestätigen
articlesDomain.acheter(panier)
' Prüfungen
Assert.AreEqual(0, articlesDomain.erreurs.Count)
Assert.AreEqual(0, panier.achats.Count)
' Artikelsuche 3
Dim unArticle As Article = articlesDomain.getArticleById(3)
' Überprüfung
Assert.AreEqual(unArticle.stockactuel, 20)
' Artikelsuche 4
unArticle = articlesDao.getArticleById(4)
' Überprüfung
Assert.AreEqual(unArticle.stockactuel, 30)
' Neuer Warenkorb
panier.ajouter(New Achat(New Article(3, "article3", 30, 30, 3), 100))
' Warenkorb bestätigen
articlesDomain.acheter(panier)
' Überprüfungen
Assert.AreEqual(1, articlesDomain.erreurs.Count)
' Artikelsuche 3
unArticle = articlesDomain.getArticleById(3)
' Überprüfung
Assert.AreEqual(unArticle.stockactuel, 20)
End Sub
<Test()> _
Public Sub testRetirerAchats()
' Inhalt von ARTICLES löschen
articlesDao.clearAllArticles()
' liest die Tabelle ARTICLES
Dim articles As IList = articlesDao.getAllArticles()
Assert.AreEqual(0, articles.Count)
' Einfügen
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)
' liest die Tabelle ARTICLES
articles = articlesDomain.getAllArticles()
Assert.AreEqual(2, articles.Count)
' Erstellung eines Warenkorbs mit zwei Artikeln
Dim monPanier As New Panier
monPanier.ajouter(New Achat(article3, 10))
monPanier.ajouter(New Achat(article4, 10))
' Prüfungen
Assert.AreEqual(700.0, monPanier.totalPanier, 0.000001)
Assert.AreEqual(2, monPanier.achats.Count)
' einen bereits gekauften Artikel hinzufügen
monPanier.ajouter(New Achat(article3, 10))
' Überprüfungen
' Der Gesamtbetrag muss auf 1000 geändert werden
Assert.AreEqual(1000.0, monPanier.totalPanier, 0.000001)
' Immer noch 2 Artikel im Warenkorb
Assert.AreEqual(2, monPanier.achats.Count)
' Die Stückzahl von Artikel 3 muss auf 20 geändert werden
Dim unAchat As Achat = CType(monPanier.achats(0), Achat)
Assert.AreEqual(20, unAchat.qte)
' Artikel 3 wird aus dem Warenkorb entfernt
monPanier.enlever(3)
' Überprüfungen
' Der Gesamtbetrag muss auf 400 geändert werden
Assert.AreEqual(400.0, monPanier.totalPanier, 0.000001)
' Nur 1 Artikel im Warenkorb
Assert.AreEqual(1, monPanier.achats.Count)
' Es muss sich um Artikel Nr. 4 handeln
Assert.AreEqual(4, CType(monPanier.achats(0), Achat).article.id)
End Sub
' Bildschirmauflistung
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
Anmerkungen:
- Wir wollten ein Testprogramm für die Schnittstelle [IArticlesDomain] schreiben, das unabhängig von der Implementierungsklasse dieser Schnittstelle ist. Daher haben wir Spring verwendet, um den Namen der Implementierungsklasse vor dem Testprogramm zu verbergen.
- Die Attributmethode <Setup()> ruft von Spring eine Referenz auf die zu testenden Objekte [articlesdomain] und [articlesdao] ab. Diese sind in der folgenden Datei [spring-config.xml] definiert:
<?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>
Diese Datei enthält:
- (Fortsetzung)
- für das Singleton [articlesdao] den Namen der Implementierungsklasse [istia.st.articles.dao.ArticlesDaoArrayList] und wo diese zu finden ist: [ webarticles-dao.dll]. Da die Instanziierung keine Parameter erfordert, sind hier keine definiert.
- Für das Singleton [articlesdomain] lautet der Name der Implementierungsklasse [istia.st.articles.domain.AchatsArticles] und sie ist in [ webarticles-domain.dll] zu finden. Die Klasse [AchatsArticles] verfügt über einen Konstruktor mit einem Parameter: das Singleton, das den Zugriff auf die Schicht [dao] verwaltet. Hier ist dieses als das zuvor definierte Singleton [articlesdao] definiert.
- Die Testklasse erhält eine Instanz der zu testenden Klasse [articlesdomain] sowie eine Instanz der Datenzugriffsklasse [articlesdao]. Dieser letzte Punkt ist umstritten. Theoretisch sollte die Testklasse keinen Zugriff auf die Schicht [dao] benötigen, die sie gar nicht kennen sollte. Hier haben wir diese „Ethik“ außer Acht gelassen, deren Einhaltung uns dazu gezwungen hätte, neue Methoden in unserer Schnittstelle [IArticlesDomain] zu erstellen.
Um die Schicht [domain] zu testen, generieren wir die Dateien DLL und [tests-webarticles-domain.dll] im Ordner [bin] des Projekts [tests]:
![]() | ![]() |
Anschließend laden wir mithilfe der Anwendung [Nunit-Gui] diese Datei DLL und führen die Tests durch:

Der Leser, der dieses Dokument auf dem Bildschirm betrachtet, wird sehen, dass alle Tests erfolgreich waren. Im weiteren Verlauf gehen wir davon aus, dass wir über eine funktionsfähige [domain]-Schicht verfügen.
1.4.5. Fazit
Zur Erinnerung: Wir möchten die folgende dreischichtige Webanwendung erstellen:
![]() |
Das Modell M unserer Anwendung MVC ist nun geschrieben und getestet. Es wird uns in zwei Dateien bereitgestellt: DLL und [webarticles-dao.dll, webarticles-domain.dll]. Wir können nun zur letzten Schicht übergehen, der Schicht [web], die den Controller C und die Views V enthält. Wir betrachten zunächst eine Methode, die im Dokument [Développement WEB avec ASP.NET 1.1 ] vorgestellt wird
- Der Controller C wird durch zwei Dateien bereitgestellt: [global.asax, main.aspx]
- Die V-Ansichten werden durch ASPX-Seiten bereitgestellt
1.5. Die Schicht [web]
Die Architektur MVC der Webanwendung sieht wie folgt aus:
![]() |
die Fachklassen [domain], die Datenzugriffsklassen [dao] und die Datenquelle | |
die Seiten ASPX | |
alle Client-Anfragen HTTP werden über die folgenden beiden Controller geleitet: global.asax: Verarbeitet Ereignisse im Zusammenhang mit dem ersten Start der Anwendung main.aspx: verarbeitet die Anfragen der einzelnen Kunden |
1.5.1. Die Ansichten
Die Ansichten entsprechen denen, die zu Beginn des Dokuments vorgestellt wurden:
liste.aspx | Die Ansichten sind im Ordner „[vues]“ der Anwendung zusammengefasst ![]() | |
infos.aspx | ||
panier.aspx | ||
paniervide.aspx | ||
erreurs.aspx |
1.5.2. Die Steuergeräte
Wie bereits erwähnt, besteht der Controller aus zwei Komponenten:
- [global.asax,global.asax.vb]: wird hauptsächlich dazu verwendet, die Anwendung zu initialisieren und alle Daten, die zwischen den verschiedenen Clients ausgetauscht werden sollen, in den Kontext der Anwendung einzubinden
- [main.aspx, main.aspx.vb]: der eigentliche Controller, der die Anfragen HTTP der Clients verarbeitet.
Die verschiedenen Anfragen der Clients werden an den Controller [main.aspx] gerichtet und enthalten einen Parameter namens [action], der die vom Client angeforderte Aktion angibt:
Anfrage | Bedeutung | Aktion des Controllers | Mögliche Antworten |
Der Kunde möchte die Liste der Artikel | – fordert die Liste der Artikel bei der an | – [LISTE] - [ERREURS] | |
Der Kunde fragt Informationen zu einem der in der Ansicht angezeigten Artikel [LISTE] | – fordert den Artikel bei der Geschäftsebene an | - [INFOS] - [ERREURS] | |
Der Kunde kauft einen Artikel | - fordert den Artikel bei der Geschäftsebene an und legt ihn in den Warenkorb des Kunden | - [INFOS] bei Mengenfehler - [LISTE], wenn kein Fehler vorliegt | |
Der Kunde möchte einen Artikel aus seinem Warenkorb entfernen | - ruft den Warenkorb aus der Sitzung ab und ändert ihn | - [PANIER] - [PANIERVIDE] - [ERREURS] | |
Der Kunde möchte seinen Warenkorb | - ruft den Warenkorb aus der Sitzung ab | - [PANIER] - [PANIERVIDE] - [ERREURS] | |
Der Kunde hat seinen Einkauf abgeschlossen und wechselt zur Zahlungsphase | - aktualisiert die Bestände der gekauften Artikel der gekauften Artikel - leert den Warenkorb des Kunden von den Artikeln, , deren Kauf bestätigt wurde | - [LISTE] - [ERREURS] |
1.5.3. Konfiguration der Anwendung
Wir werden versuchen, die Anwendung so zu konfigurieren, dass sie gegenüber Änderungen wie den folgenden möglichst flexibel ist:
- Änderungen an den URL der verschiedenen Ansichten
- die Änderung der Klassen, die die Schnittstellen [IArticlesDao] und [IArticlesDomain] implementieren
- Änderung des SGBD, der Datenbank und der Artikeltabelle
1.5.3.1. Änderungen an URL
Die Namen der Ansichten URL werden zusammen mit einigen weiteren Parametern in die Anwendungskonfigurationsdatei [web.config] aufgenommen:
<?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. Änderung der Implementierungsklassen von Schnittstellen
Im Sinne der dreischichtigen Architektur müssen die Schichten voneinander isoliert sein. Diese Isolierung wird wie folgt erreicht:
- Die Schichten kommunizieren untereinander über Schnittstellen und nicht über konkrete Klassen
- Der Code einer Schicht instanziiert niemals selbst die Klasse einer anderen Schicht, um sie zu verwenden. Er fordert lediglich von einem externen Tool – in diesem Fall Spring – eine Instanz der Implementierung der Schnittstelle der Schicht an, die er nutzen möchte. Dazu muss er nicht den Namen der Implementierungsklasse kennen, sondern lediglich den Namen des Spring-Singletons, auf das er verweisen möchte.
In unserer Anwendung wird Spring in der Datei [web.config] der Webanwendung wie folgt konfiguriert:
<?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>
Um Zugriff auf die Schicht [métier] zu erhalten, kann eine Klasse der Schicht [web] das Singleton [articlesDomain] anfordern. Spring instanziiert dann ein Objekt vom Typ [istia.st.articles.domain.AchatsArticles]. Für diese Instanziierung benötigt es ein Objekt vom Typ [articlesDao], d. h. ein Objekt vom Typ [istia.st.articles.dao.ArticlesDaoArrayList]. Spring instanziiert dann ein solches Objekt. Am Ende des Vorgangs verfügt die Schicht [web], die das Singleton [articlesDomain] angefordert hat, über die gesamte Kette, die sie mit der Datenquelle verbindet:
![]() |
1.5.3.3. Änderungen im Zusammenhang mit SGBD oder der Datenbank
Dieser Punkt wird hier außer Acht gelassen, da wir uns in einer Testanwendung ohne SGBD befinden. In einem zweiten Schritt werden wir die Implementierung einer Schicht [dao] behandeln, die auf einem SGBD basiert.
1.5.4. Die <asp:>-Tag-Bibliothek
Betrachten wir die Ansicht [ERREURS], die eine Fehlerliste anzeigt:

Die Ansicht [ERREURS] ist dafür zuständig, eine Fehlerliste anzuzeigen, die das Controller-Element [main.aspx] unter dem Namen [context.Items("erreurs")] in den Anforderungskontext gestellt hat. Es gibt mehrere Möglichkeiten, eine solche Seite zu schreiben. Wir befassen uns hier ausschließlich mit dem Teil zur Anzeige der Fehler.
Zur Erinnerung: Eine Seite ASPX besteht aus einem Layout-Teil HTML und einem Code-Teil .NET, der die Daten aufbereitet, die der Layout-Teil anzeigen soll. Diese beiden Teile können sich in derselben Datei [aspx] (Lösung WebMatrix) oder in zwei Dateien befinden: [aspx] für die Darstellung, [aspx.vb] für den Code. Letztere Lösung wird von Visual Studio verwendet. Um die Sache noch komplizierter zu machen, kann der Layout-Teil HTML ebenfalls Code enthalten (NET), was die Trennung zwischen [contrôleur] und [présentation] aus der Ansicht heraus verwischt. Von dieser Vorgehensweise wird im Allgemeinen dringend abgeraten. Um den gesamten Code aus dem [présentation]-Teil zu entfernen, mussten Tag-Bibliotheken erstellt werden. Diese „verbergen“ den Code unter dem Deckmantel von Tags, die den HTML-Tags ähneln. Wir stellen zwei mögliche Lösungen für die Seite [ERREURS] vor.
Unsere erste Lösung verwendet Code aus .NET im Teil [présentation] der Seite. Die Seite ASPX ruft die in der Anfrage enthaltene Fehlerliste in ihrem Controller-Teil [erreurs.aspx.vb] ab:
Protected erreurs As ArrayList
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
' Fehler werden abgerufen
erreurs = CType(context.Items("erreurs"), ArrayList)
End Sub
und zeigt es dann im Bereich [présentation, erreurs.aspx] an:
<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>
Die zweite Lösung nutzt das Tag <asp:repeater> aus der Tag-Bibliothek <asp:> von ASP.NET. Wenn man eine Seite ASPX grafisch erstellt, steht dieses Tag als Serverkomponente zur Verfügung, die man auf das Entwurfsformular zieht. Wenn man den Code ASPX manuell erstellt, spricht man von einer Tag-Bibliothek.
Mit der Tag-Bibliothek <asp:> sieht der Code ASPX der vorherigen Ansicht [ERREURS] wie folgt aus:
<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>
Das Tag
dient dazu, ein Muster HTML auf die verschiedenen Elemente einer Datenquelle anzuwenden. Die verschiedenen Elemente lauten wie folgt:
das Muster HTML, das angezeigt werden soll, bevor die Elemente der Datenquelle angezeigt werden | |
das Muster HTML, das für jedes Element der Datenquelle wiederholt werden soll. Der Ausdruck [<%# Container.DataItem %>] dient dazu, den Wert des aktuellen Elements der Datenquelle anzuzeigen | |
Das Muster HTML wird angezeigt, nachdem die Elemente der Datenquelle angezeigt wurden |
Die Datenquelle wird in der Regel im Abschnitt [contrôleur] der Seite mit dem Tag verknüpft:
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
..
' Die Fehler werden mit rptErreurs verknüpft
With rptErreurs
.DataSource = context.Items("erreurs")
.DataBind()
End With
End Sub
Diese Verknüpfung kann auch bereits bei der Gestaltung der Seite hergestellt werden, wenn die Datenquelle bereits bekannt ist, beispielsweise eine bestehende Datenbank.
In unseren Ansichten verwenden wir ein anderes Tag: <asp:datagrid>, mit dem eine Datenquelle in Form einer Tabelle angezeigt werden kann.
1.5.5. Struktur der Visual Studio-Lösung der Anwendung [webarticles]
Eine Webanwendung ist wie ein Puzzle mit vielen Elementen. Die Verwendung einer MVC-Architektur erhöht in der Regel deren Anzahl. Die Struktur der Anwendung [webarticles] unter [Visual Studio] sieht wie folgt aus:
![]() | ![]() | ![]() |
![]() |
Anmerkungen:
- Das Projekt [web] ist vom Typ [bibliothèque de classes] und nicht vom Typ [Application web ASP.NET], wie man logischerweise erwarten würde. Der Typ [Application web ASP.NET] setzt voraus, dass der Webserver IIS auf dem Entwicklungsrechner oder auf einem Remote-Rechner vorhanden ist. Der Server IIS ist standardmäßig auf Windows-Rechnern der Version XP „Familial“ nicht vorhanden. Viele Rechner der Version PC werden jedoch mit dieser Version verkauft. Damit Leser mit Windows XP die behandelte Anwendung implementieren können, verwenden wir den Webserver Cassini (siehe Anhang), der kostenlos bei Microsoft erhältlich ist, und ersetzen das Projekt [Application web ASP.NET] durch ein Projekt [bibliothèque de classes]. Dies bringt einige Nachteile mit sich, die im Anhang erläutert werden.
- Die von der Anwendung verwendeten DLL-Dateien sind folgende:
umfasst die Klassen der Datenzugriffsebene | |
fasst die Klassen der Geschäftssschicht zusammen | |
enthält die Spring-Klassen, mit denen wir die Web-, Domain- und DAO-Schichten integrieren können | |
Protokollierungsklassen – werden von Spring verwendet |
Diese DLL-Dateien werden im Ordner [bin] abgelegt und den Projektverweisen hinzugefügt.
1.5.6. Die Ansichten ASPX
Wie bereits empfohlen, werden wir in unseren Ansichten ASPX die <asp:>-Tag-Bibliothek verwenden.
1.5.6.1. Die Benutzerkomponente [entete.ascx]
Um den verschiedenen Ansichten eine gewisse Einheitlichkeit zu verleihen, werden diese denselben Kopfbereich verwenden, in dem der Name der Anwendung zusammen mit dem Menü angezeigt wird:
![]() | ![]() |
Das Menü ist dynamisch und wird vom Controller festgelegt. Dieser fügt in die an die Seite ASPX übermittelte Anfrage ein Schlüsselattribut „actions“ ein, dessen zugehöriger Wert ein Array von Elementen vom Typ Hashtable() ist. Jedes Element dieses Arrays ist ein Dictionary, das dazu dient, eine Option des Kopfzeilenmenüs zu generieren. Jedes Dictionary hat zwei Schlüssel:
- href: die mit der Menüoption verknüpfte URL
- link: der Menütext
Wir werden die Kopfzeile zu einem Benutzer-Steuerelement machen. Ein Benutzer-Steuerelement kapselt einen Teil der Seite (Darstellung und zugehöriger Code) in einer Komponente, die anschließend auf anderen Seiten wiederverwendet werden kann. Hier möchten wir die Komponente [entete] in den anderen Ansichten der Anwendung wiederverwenden. Der Layout-Code befindet sich in [entete.ascx] und der zugehörige Steuerungscode in [entete.ascx.vb]. Der Layout-Code verwendet eine <asp:repeater>-Komponente, um die Tabelle mit den Menüoptionen anzuzeigen:
![]() |
n° | type | nom | rôle |
1 | Repeater | rptMenu Datenquelle: ein Array von Dictionaries mit zwei Schlüsseln: href, Link | Menüoptionen anzeigen |
Der Code zur Darstellung der Seite lautet wie folgt:
Anmerkungen:
- Die Komponente [repeater] ist in den Zeilen 6–14 definiert
- Jedes Element der dem Repeater zugeordneten Datenquelle ist ein Wörterbuch mit zwei Schlüsseln: href – Zeile 9 und link – Zeile 10
Der zugehörige Steuerungscode lautet wie folgt:
Anmerkungen:
- Die Komponente vom Typ [EnteteWebArticles] verfügt über eine öffentliche, schreibgeschützte Eigenschaft [actions] – Zeile 7
- Diese Eigenschaft ermöglicht es, der <asp:repeater>-Komponente mit dem Namen [rptMenu] – Zeile 10 – das vom Anwendungscontroller berechnete Optionsarray – Zeilen 11–12 – zuzuordnen.
Die anderen Ansichten der Anwendung verwenden den von [entete.ascx] definierten Header. Die Seite [erreurs.aspx] beispielsweise bindet den Header mithilfe des folgenden Codes ein:
Anmerkungen:
- Zeile 1 legt fest, dass das Tag <WA:entete> der durch die Datei [entete.ascx] definierten Komponente zugeordnet werden soll. Die Attribute [TagPrefix] und [TagName] können frei gewählt werden.
- Anschließend erfolgt das Einfügen der Komponente in den Layout-Code der Seite mit Zeile 9. Bei der Ausführung bewirkt dieses Tag, dass in den Code der Seite ASPX, die sie enthält, der Code der Seite [entete.ascx] eingebunden wird. Der Steuerungscode [erreurs.aspx.vb] sorgt für die Initialisierung dieser Komponente. Dies kann auf folgende Weise erfolgen:
Anmerkungen:
- In Zeile 6 wird ein Objekt vom Typ [EnteteWebArticles] erstellt, was dem Typ der erstellten Komponente entspricht
- Zeile 11 initialisiert die Eigenschaft [actions] dieses Objekts
1.5.6.2. Die Ansicht [liste.aspx]
1.5.6.2.1. Introduction
Diese Ansicht zeigt die Liste der zum Verkauf verfügbaren Artikel an:
![]() | ![]() |
Sie wird nach einer Anfrage an /main?action=liste oder /main?action=validationpanier angezeigt. Die Elemente der Controller-Anfrage lauten wie folgt:
Objekt Hashtable() – die Tabelle der Menüoptionen | |
ArrayList Objekte vom Typ [Article] | |
String-Objekt – am Seitenende anzuzeigende Meldung |
Jeder Link [Infos] aus der Tabelle HTML der Artikel hat eine URL in der Form [?action=infos&id=ID], wobei ID das Feld „id“ desangezeigten Artikels ist.
1.5.6.2.2. Seitenkomponenten
![]() |
n° | type | nom | rôle |
Benutzerkomponente | Kopfzeile | Kopfzeile anzeigen | |
DataGrid | DataGridArticles 3 – zugehörige Spalte: Überschrift: Name, Feld: Name 4 – zugehörige Spalte: Überschrift: Preis, Feld: Preis 5 – Hypertext-Spalte: Text: Infos, URL-Feld: id, Format URL: /webarticles/main.aspx?action=infos&id={0} | Artikel im Verkauf anzeigen | |
Bezeichnung | lblMessage | Eine Nachricht anzeigen |
Zur Erinnerung: So legen Sie diese Eigenschaften fest:
- In Visual Studio wählen Sie „[DataGrid]“ aus, um Zugriff auf dessen Eigenschaftenblatt zu erhalten:

- Verwenden Sie den oben genannten Link [Mise en forme automatique], um das Layout der angezeigten Tabelle zu verwalten
- und den Link „[Générateur de propriétés]“, um dessen Inhalt zu bearbeiten
1.5.6.2.3. Darstellungscode [liste.aspx]
Anmerkungen:
- Zeile 9 definiert die Kopfzeile der Seite
- Die Zeilen 12–24 definieren die Merkmale von [DataGrid]
- Zeile 26 definiert die Bezeichnung [lblMessage]
1.5.6.2.4. Controller-Code [liste.aspx.vb]
Anmerkungen:
- Die Komponenten der Seite erscheinen in den Zeilen 13–15. Zu beachten ist, dass ein Objekt [EnteteWebArticles] mit einem Operator [new] angelegt werden musste, während dies bei den anderen Komponenten nicht erforderlich war. Ohne diese explizite Erstellung trat ein Laufzeitfehler auf, der darauf hinwies, dass das Objekt [entete] auf nichts verwies. Dieser Punkt wäre einer genaueren Untersuchung wert gewesen. Dies geschah jedoch nicht.
- Die Tabelle mit den Optionen des Kopfmenüs wird aus dem Kontext übernommen, um die Komponente [entete] der Seite zu initialisieren – Zeile 20
- Die Artikelliste wird aus dem Kontext übernommen – Zeile 22
- zur Initialisierung der Komponente [DataGridArticles] – Zeilen 24–27
- Die Komponente [lblMessage] wird mit einer im Kontext hinterlegten Meldung initialisiert – Zeile 29
1.5.6.3. Die Ansicht [infos.aspx]
1.5.6.3.1. Introduction
Diese Ansicht zeigt Informationen zu einem Artikel an und ermöglicht zudem dessen Kauf:

Sie wird nach einer Anfrage /main?action=infos&id=ID oder einer Anfrage /main?action=achat&id=ID angezeigt, wenn die gekaufte Menge falsch ist. Die Elemente der Controller-Anfrage lauten wie folgt:
Objekt Hashtable() – die Tabelle der Menüoptionen | |
Objekt vom Typ [Article] – anzuzeigender Artikel | |
String-Objekt – Meldung, die bei einem Fehler bezüglich der Menge angezeigt werden soll | |
String-Objekt – im Eingabefeld anzuzeigender Wert [Qte] |
Die Felder [msg] und [qte] werden bei einem Eingabefehler bei der Menge verwendet:

Diese Seite enthält ein Formular, das über die Schaltfläche [Acheter] übermittelt wird. Das Ziel von URL ist POST, wobei [?action=achat&id=ID] die ID des gekauften Artikels ist.
1.5.6.3.2. Die Komponenten der Seite
![]() |
n° | type | nom | rôle |
Benutzerkomponente | Kopfzeile | Kopfzeile anzeigen | |
Literal | litID | Artikelnummer anzeigen | |
DataGrid | DataGridArticle 3 – zugehörige Spalte: Überschrift: Name, Feld: Name 4 – zugehörige Spalte: Überschrift: Preis, Feld: Preis 5 – zugehörige Spalte: Überschrift: Aktueller Bestand, Feld: stockActuel 6 – zugehörige Spalte: Überschrift: Mindestbestand, Feld: stockMinimum | Artikel anzeigen | |
HTML Absenden | Formular absenden | ||
HTML Eingabe runat=server | txtQte | Gekaufte Menge eingeben | |
Bezeichnung | lblMsgQte | Eventuelle Fehlermeldung |
1.5.6.3.3. Der Präsentationscode [infos.aspx]
Anmerkungen:
- Der Header ist in der Seite enthalten – Zeile 9
- Die Konstante [litId] ist in Zeile 10 definiert
- DataGrid und [DataGridArticles] sind in den Zeilen 12–34 definiert
- Das Formular ist in den Zeilen 36–46 definiert. Es hat den Typ POST.
- Das Ziel von POST wird durch die Variable [strAction] bereitgestellt – Zeile 36. Diese Variable muss vom Controller definiert werden.
- Das Eingabefeld für die gekaufte Menge ist in Zeile 41 definiert. Es handelt sich um eine Serverkomponente HTML (runat=server). Im Code kann über ein Objekt darauf zugegriffen werden.
- In Zeile 42 wird die Beschriftung [lblMsgQte] definiert, die eine eventuelle Fehlermeldung zur eingegebenen Menge enthält.
1.5.6.3.4. Der Steuerungscode [infos.aspx.vb]
Anmerkungen:
- Die Komponenten der Seite werden in den Zeilen 10–14 definiert
- Die Klasse definiert eine öffentliche Eigenschaft [strAction], die dazu dient, das Ziel des POST des Formulars festzulegen – Zeilen 17–25
- Der anzuzeigende Artikel wird aus dem Anwendungskontext abgerufen – Zeile 30
- Die Tabelle mit den Optionen für das Kopfmenü wird aus dem Kontext übernommen, um die Komponente [entete] der Seite zu initialisieren – Zeile 32
- Zeilen 33–39: Die Komponente [DataGridArticle] wird mit einer Datenquelle vom Typ [ArrayList] verknüpft, die ausschließlich den in Zeile 30 abgerufenen Artikel enthält
- Die Komponenten [lblMsgQte, txtQte] werden mit Informationen aus dem Kontext initialisiert – Zeilen 42–45
- Die Eigenschaft [straction] wird ebenfalls mit einer aus dem Kontext entnommenen Information initialisiert – Zeile 47. Diese Variable dient dazu, das Attribut [action] des Formulars HTML zu generieren, das auf der Seite vorhanden ist:
1.5.6.4. Die Ansicht [panier.aspx]
1.5.6.4.1. Introduction
Diese Ansicht zeigt den Inhalt des Warenkorbs an:

Sie wird nach einer Anfrage an /main?action=panier oder /main?action=retirerachat&id=ID angezeigt. Die Elemente der Controller-Anfrage lauten wie folgt:
Objekt „Hashtable()“ – die Tabelle mit den Menüoptionen | |
Objekt vom Typ [Panier] – der anzuzeigende Warenkorb |
Jeder Link [Retirer] aus dem Array HTML der Warenkorb-Artikel hat einen URL der Form [?action=retirerachat&id=ID], wobei ID das Feld [id] des Artikels, den man aus dem Warenkorb entfernen möchte.
1.5.6.4.2. Die Komponenten der Seite
![]() |
n° | type | nom | rôle |
Benutzerkomponente | Kopfzeile | Kopfzeile anzeigen | |
DataGrid | DataGridAchats 3 – zugehörige Spalte – Überschrift: Artikel, Feld: Name 4 – Verknüpfte Spalte – Überschrift: Menge, Feld: Menge 5 – zugehörige Spalte – Überschrift: Preis, Feld: Preis 6 – zugehörige Spalte – Überschrift: Gesamt, Feld: Gesamt, Formatierung {0:C} 7 – Hypertext-Spalte – Text: Entfernen, URL: id, URL-Format: /webarticles/main.aspx?action=retirerachat&id={0} | Liste der gekauften Artikel anzeigen | |
Bezeichnung | lblTotal | Zu zahlenden Betrag anzeigen |
1.5.6.4.3. Der Darstellungscode [panier.aspx]
Anmerkungen
- Zeile 9 enthält die Kopfzeile
- In den Zeilen 12–27 wird die Komponente [DataGridAchats] definiert
- In Zeile 29 wird die Komponente [lblTotal] definiert
1.5.6.4.4. Der Prüfcode [panier.aspx.vb]
Anmerkungen:
- Die Komponenten der Seite werden in den Zeilen 11–13 deklariert
- Die Initialisierung der Komponente [entete] entspricht derjenigen auf den bereits behandelten Seiten – Zeile 17
- Der anzuzeigende Warenkorb wird aus der Sitzung abgerufen – Zeile 19
- Die Anzeige dieses Warenkorbs mithilfe der Komponente [DataGridAchats] bereitet Probleme. Die Schwierigkeit liegt in der Initialisierung der Komponente. Erinnern wir uns an deren Spalten:
- Spalte [Article], die dem Feld [nom] der Datenquelle zugeordnet ist
- Spalte [Qté], die dem Feld [qte] der Datenquelle zugeordnet ist
- Spalte [Prix], zugeordnet zu einem Feld [prix] der Datenquelle
- Spalte [Total], die dem Feld [total] der Datenquelle zugeordnet ist
Die uns zur Verfügung stehende Datenquelle ist der Warenkorb und die dazugehörige Einkaufsliste. Letztere dient als Datenquelle für [DataGrid]. Nur die Objekte [Achat], die die Zeilen des [DataGrid] füllen sollen, weisen nicht die vom [DataGrid] erwarteten Eigenschaften des [nom, qte, prix, total] auf. Daher wird hier speziell für das Objekt [DataGrid] eine Datenquelle angelegt, deren Elemente die vom Objekt [DataGrid] erwarteten Eigenschaften aufweisen. Diese Elemente werden vom Typ [LigneAchat] sein, einer eigens für diesen Zweck erstellten Klasse, die von der Klasse [Achat] abgeleitet ist – Zeilen 36–66
- Nachdem die Klasse [LigneAchat] definiert wurde, wird die Datenquelle von [DataGridAchats] aus dem in der Sitzung gefundenen Warenkorb erstellt – Zeilen 20–30
- Der Einkaufsbetrag wird mithilfe der Eigenschaft [totalPanier] der Klasse [Panier] angezeigt – Zeile 32
1.5.6.5. Die Ansicht [paniervide.aspx]
1.5.6.5.1. Introduction
Diese Ansicht zeigt die Information an, dass der Warenkorb leer ist:

Sie wird nach einer Anfrage an /main?action=warenkorb oder /main?action=retirerachat&id=ID angezeigt. Die Elemente der Controller-Anfrage lauten wie folgt:
Objekt „Hashtable()“ – die Tabelle mit den Menüoptionen |
1.5.6.5.2. Die Komponenten der Seite
![]() |
n° | type | nom | rôle |
Benutzerkomponente | Kopfzeile | Kopfzeile anzeigen |
1.5.6.5.3. Der Darstellungscode [paniervide.aspx]
Anmerkungen:
- Die Kopfzeile ist in Zeile 9 enthalten
1.5.6.5.4. Der Prüfcode [paniervide.aspx.vb]
Kommentare:
- Hier wird lediglich die einzige dynamische Komponente der Seite initialisiert – Zeile 10
1.5.6.6. Die Ansicht [erreurs.aspx]
1.5.6.6.1. Introduction
Diese Ansicht wird bei Fehlern angezeigt:

Sie wird nach jeder Anfrage angezeigt, die zu einem Fehler führt, mit Ausnahme der Kaufaktion mit einer falschen Menge, die hingegen von der Ansicht [INFOS] verarbeitet wird. Die Elemente der Anfrage des Controllers sind folgende:
Objekt Hashtable() – die Tabelle der Menüoptionen | |
ArrayList Objekte vom Typ [String], die die anzuzeigenden Fehlermeldungen darstellen |
1.5.6.6.2. Die Komponenten der Seite
![]() |
n° | type | nom | rôle |
Benutzerkomponente | Kopfzeile | Kopfzeile anzeigen | |
Repeater | rptErreurs | Fehlerliste anzeigen |
1.5.6.6.3. Der Darstellungscode [erreurs.aspx]
Anmerkungen:
- Der Header ist in Zeile 9 definiert
- Die Komponente [rptErreurs] ist in den Zeilen 13–19 definiert. Ihr Inhalt stammt aus einer Datenquelle vom Typ [ArrayList] mit Objekten vom Typ [String].
1.5.6.6.4. Der Prüfcode [erreurs.aspx.vb]
Anmerkungen:
- Die Komponente [entete] wird wie üblich initialisiert (Zeilen 9 und 14)
- Die Komponente [rptErreurs] wird mit der im Kontext gefundenen Fehlerliste vom Typ [ArrayList] initialisiert – Zeilen 16–19
1.5.7. Die Controller global.asax, main.aspx
Nun muss noch das Herzstück unserer Webanwendung geschrieben werden: der Controller. Seine Aufgabe besteht darin:
- die Anfrage des Clients entgegenzunehmen,
- die vom Kunden angeforderte Aktion mithilfe der Geschäftsklassen zu verarbeiten,
- als Antwort die entsprechende Ansicht zurückzusenden.
1.5.7.1. Der Controller [global.asax.vb]
Wenn die Anwendung ihre allererste Anfrage erhält, wird die Prozedur [Application_Start] aus der Datei [global.asax.vb] ausgeführt. Dies geschieht nur ein einziges Mal. Die Prozedur [Application_Start] dient dazu, die für die Webanwendung erforderlichen Objekte zu initialisieren, die von allen Client-Threads schreibgeschützt gemeinsam genutzt werden. Diese gemeinsam genutzten Objekte können an zwei Stellen abgelegt werden:
- in den privaten Feldern des Controllers
- im Ausführungskontext der Anwendung (Application)
Die Methode [Application_Start] der Anwendung [global.asax.vb] führt folgende Aktionen durch:
- Sie prüft, ob in der Datei [web.config] die für den ordnungsgemäßen Betrieb der Anwendung erforderlichen Parameter vorhanden sind. Diese wurden in Abschnitt 1.5.3 beschrieben.
- überträgt die Liste eventueller Fehler in Form eines Objekts vom Typ [ArrayList erreurs] in den Anwendungskontext. Diese Liste ist leer, wenn keine Fehler vorliegen, existiert aber dennoch.
- Wenn Fehler aufgetreten sind, endet die Methode [Application_Start] an dieser Stelle. Andernfalls fordert sie die Referenz eines Singletons vom Typ [IArticlesDomain] an, bei dem es sich um das Geschäftsobjekt handelt, das der Controller für seine Zwecke verwenden wird. Wie in 1.5.3.2 erläutert, fordert der Controller dieses Singleton vom Spring-Framework an. Bei dieser Instanziierung können verschiedene Fehler auftreten. Ist dies der Fall, werden diese wiederum im Objekt [erreurs] des Anwendungskontexts gespeichert.
Der Controller [global.asax.vb] verfügt über eine Prozedur [Session_Start], die bei jedem neuen Kunden aufgerufen wird. In dieser Prozedur wird ein leerer Warenkorb für den Kunden angelegt. Dieser Warenkorb wird über alle Anfragen dieses bestimmten Kunden hinweg beibehalten. Der Code könnte wie folgt aussehen:
Anmerkungen:
- Die in [web.config] erwarteten Parameter sind in einem Array definiert – Zeile 18
- Sie werden in [web.config] gesucht. Sind sie vorhanden, werden sie im Anwendungskontext gespeichert, andernfalls wird ein Fehler in der Fehlerliste [erreurs] – Zeilen 21–33 – protokolliert
- Wenn keine Fehler vorliegen, wird von Spring eine Referenz auf das Singleton [articlesDomain] angefordert, das den Zugriff auf die Anwendungsschicht [domain] verwaltet – Zeilen 35–47. Eventuelle Fehler werden in [erreurs] protokolliert.
- Die Fehler werden im Anwendungskontext gespeichert – Zeile 49
- Die Prozedur wird bei Fehlern beendet – Zeile 51
- Es wird ein Array aus drei Dictionaries erstellt. Jedes davon hat zwei Schlüssel: href und link. Dieses Array repräsentiert die drei möglichen Menüoptionen – Zeilen 52–71
- Dieses Array wird im Anwendungskontext gespeichert – Zeile 73
- Bei jedem neuen Kunden wird die Prozedur [Session_Start] ausgeführt. Dort wird in der Sitzung des Kunden ein leerer Warenkorb angelegt – Zeilen 78–81
1.5.7.2. Der Controller [main.aspx.vb]
Der Controller [main.aspx.vb] empfängt alle Anfragen der Kunden. Diese haben nämlich alle die Form [/webarticles/main.aspx?action=XX]. Eine Anfrage wird wie folgt verarbeitet:
- Das Objekt [erreurs] aus dem Anwendungskontext wird überprüft. Ist es nicht leer, bedeutet dies, dass bei der Initialisierung der Anwendung Fehler aufgetreten sind und diese nicht funktionieren kann. Als Antwort wird dann die Ansicht [ERREURS] gesendet.
- Der Parameter [action] der Anfrage wird abgerufen und überprüft. Entspricht er keiner bekannten Aktion, wird die Ansicht [ERREURS] mit einer entsprechenden Fehlermeldung gesendet.
- Ist der Parameter [action] gültig, wird die Anfrage des Clients zur Bearbeitung an eine aktionsspezifische Prozedur weitergeleitet:
Methode | Anfrage | Verarbeitung | Mögliche Antworten |
GET /main?action=liste | - Liste der Artikel bei der Fachklasse - anzeigen | [LISTE] oder [ERREURS] | |
GET /main?action=infos&id=ID | - den Artikel mit der ID = ID bei der Fachklasse - anzeigen | [INFOS] oder [ERREURS] | |
POST /main?action=Kauf&id=ID - Die gekaufte Menge ist Teil der übermittelten Parameter | - den Artikel mit der ID=ID bei der Fachklasse abfragen - in den Warenkorb aufnehmen, und zwar der Kundensitzung | [LISTE] oder [INFOS] oder [ERREURS] | |
GET /main?action=retirerachat&id=ID | - Artikel mit der ID=ID aus der Einkaufsliste des Warenkorbs der Kundensitzung | [PANIER] | |
GET /main?action=warenkorb | - Warenkorb der Kundensitzung | [PANIER] oder [PANIERVIDE] | |
GET /main?action=warenkorbbestätigung | - die Lagerbestände aller Artikel in der Datenbank verringern Bestände aller Artikel , die sich im Warenkorb der Kunden-Sitzung | [LISTE] oder [ERREURS] |
Das Grundgerüst des Controllers [main.aspx.vb] könnte wie folgt aussehen:
Anmerkungen:
- Die Klasse verfügt über zwei private Felder, die von den Methoden gemeinsam genutzt werden – Zeilen 15–16:
- articlesDomain: das Singleton für den Zugriff auf die Schicht [domain]
- Optionen: das Array der Dictionaries für die Menüoptionen
- Die Prozedur [Page_Load]:
- initialisiert die beiden privaten Felder der Klasse
- ruft den Parameter [action] aus der Anfrage ab und lässt die Verarbeitungsmethode für diese Aktion ausführen.
1.5.7.3. Die Methode [Page_Load]
Dieses Ereignis tritt als erstes auf der Seite auf. Der Code lautet wie folgt:
Anmerkungen:
- Bei jedem Laden der Seite wird sichergestellt, dass die von [global.asax] durchgeführte Initialisierung der Anwendung erfolgreich verlaufen ist.
- Dazu wird aus dem Anwendungskontext die von [global.asax] – Zeile 4 – dort hinterlegte Fehlerliste abgerufen.
- Ist diese Liste nicht leer, wird die Ansicht [ERREURS] angezeigt – Zeilen 6–10
- Man ruft das von [global.asax] im Anwendungskontext abgelegte Singleton [articlesDomain] ab und speichert es im privaten Feld [articlesDomain], damit es den verschiedenen Methoden der Klasse zur Verfügung steht – Zeile 12
- Ähnliches wird mit dem Array der Menüoptionen durchgeführt – Zeile 14
- Der Parameter [action] wird aus der Anfrage abgerufen – Zeile 16
- Die der angeforderten Aktion entsprechende Methode wird ausgeführt. Eine nicht vorgesehene Aktion wird als Aktion [liste] betrachtet – Zeilen 16–36
1.5.7.4. Verarbeitung der Aktion [liste]
Hierbei geht es um die Anzeige der Artikelliste:

Der Code lautet wie folgt:
Anmerkungen:
- Eventuelle Fehler werden in ein [ArrayList] geschrieben – Zeile 4
- Die Artikelliste wird vom Singleton [articlesDomain] angefordert – Zeilen 5–12
- Wenn Fehler aufgetreten sind, wird die Ansicht [ERREURS] gesendet – Zeilen 13–19
- ansonsten wird die Ansicht [LISTE] gesendet – Zeilen 20–24
1.5.7.5. Verarbeitung der Aktion [infos]
Der Kunde hat Informationen zu einem bestimmten Artikel angefordert:

Der Code lautet wie folgt:
Anmerkungen:
- Eventuelle Fehler werden in ein [ArrayList] – Zeile 4 – geschrieben
- Die ID des angeforderten Artikels wird aus der Anfrage abgerufen – Zeile 6
- Diese ID wird überprüft. Sie muss vorhanden sein und es muss sich um eine Ganzzahl handeln. Ist dies nicht der Fall, wird die Ansicht [ERREURS] mit der entsprechenden Fehlermeldung gesendet – Zeilen 7–27
- Sobald die ID überprüft wurde, wird der Artikel beim Singleton [articlesDomain] angefordert. Tritt dabei eine Ausnahme auf, wird die Ansicht [ERREURS] gesendet – Zeilen 29–39
- Wurde der Artikel nicht gefunden, wird die Ansicht [ERREURS] gesendet – Zeilen 41–48
- Wenn der Artikel gefunden wurde, wird er in die Sitzung des Benutzers aufgenommen und anschließend in der Ansicht [INFOS] angezeigt – Zeilen 50–56
1.5.7.6. Verarbeitung der Aktion [achat]
Der Kunde hat den in der Ansicht [INFOS] angezeigten Artikel gekauft.

Der Code lautet wie folgt:
Anmerkungen:
- Der in die Sitzung geladene Artikel wird abgerufen – Zeile 5
- Wenn er nicht vorhanden ist (die Sitzung ist möglicherweise abgelaufen), wird die Ansicht [LISTE] angezeigt – Zeilen 7–10
- Die gekaufte Menge wird aus der Abfrage abgerufen – Zeile 12
- Die Gültigkeit wird überprüft – Zeilen 13–29
- Im Falle einer Ungültigkeit wird je nach Situation entweder die Ansicht [LISTE] – Zeile 16 oder die Ansicht [INFOS] – Zeilen 24–28 – gesendet
- Wenn alles in Ordnung ist, wird der Kauf im Warenkorb gespeichert – Zeilen 31–32
- Anschließend wird die Ansicht [LISTE] gesendet – Zeile 34
1.5.7.7. Verarbeitung der Aktion [panier]
Der Kunde hat verschiedene Artikel gekauft und möchte den Warenkorb einsehen:

Der Code lautet wie folgt:
Anmerkungen:
- Der Warenkorb wird aus der Sitzung abgerufen – Zeile 4. Hier wird nicht überprüft, ob tatsächlich etwas abgerufen wird. Dies wäre jedoch erforderlich, da die Sitzung möglicherweise abgelaufen ist.
- Wenn der Warenkorb leer ist, wird die Ansicht [PANIERVIDE] gesendet – Zeilen 6–10
- Andernfalls wird die Ansicht [PANIER] gesendet – Zeilen 11–14
1.5.7.8. Verarbeitung der Aktion [retirerachat]
Der Kunde möchte einen Artikel aus seinem Warenkorb entfernen:

Der Code lautet wie folgt:
Anmerkungen:
- Der Warenkorb wird aus der Sitzung abgerufen – Zeile 4. Hier wird nicht überprüft, ob tatsächlich etwas abgerufen wurde. Dies wäre jedoch erforderlich, da die Sitzung möglicherweise abgelaufen ist.
- In der Abfrage wird die ID [id] des zu entfernenden Artikels abgerufen – Zeile 8.
- Der entsprechende Kauf wird aus dem Warenkorb entfernt – Zeile 10
- Die Gültigkeit der ID des gekauften Artikels wurde hier nicht überprüft. Wenn diese einen ungültigen Typ hat, wird eine Ausnahme ausgelöst und in den Zeilen 11–13 behandelt. Wenn sie gültig ist, aber nicht existiert, führt die Methode [panier.enlever] – Zeile 10 – keine Aktion aus.
- Der neue Warenkorb wird angezeigt – Zeile 16
1.5.7.9. Verarbeitung der Aktion [validerpanier]
Der Kunde möchte seinen Warenkorb bestätigen:

Der Code lautet wie folgt:
Anmerkungen:
- Der Warenkorb wird aus der Sitzung abgerufen – Zeile 6. Hier wird nicht überprüft, ob tatsächlich etwas abgerufen wurde. Dies wäre jedoch erforderlich, da die Sitzung möglicherweise abgelaufen ist.
- Falls die Sitzung abgelaufen ist, haben wir einen Zeiger [nothing] für den Warenkorb, und die Methode [acheter] – Zeile 9 – löst eine Ausnahme aus, woraufhin die Ansicht [ERREURS] gesendet wird. Allerdings ist die Fehlermeldung für den Benutzer wenig aussagekräftig.
- In den Zeilen 8–16 wird versucht, den aus der Sitzung abgerufenen Warenkorb zu validieren. Bestimmte Käufe können möglicherweise nicht bestätigt werden, wenn die angeforderte Menge den Lagerbestand des gewünschten Artikels übersteigt. Diese Fälle werden von der Methode [acheter] in einer Fehlerliste gespeichert, die in Zeile 11 abgerufen wird.
- Liegen Fehler vor, wird die Ansicht [ERREURS] gesendet – Zeilen 18–23
- Andernfalls wird die Ansicht [LISTE] gesendet – Zeilen 25–26
1.6. Conclusion
Wir haben hier eine Anwendung nach dem Muster MVC entwickelt. Es scheint (Stand: April 2005) keine professionellen Entwicklungs-„Frameworks“ für MVC in ASP.NET zu geben, wie es sie in Java gibt (Struts, Spring, ...). Das Projekt [Spring.net] dürfte in Kürze ein solches anbieten. Bis dahin ermöglicht die oben beschriebene Methode eine praktikable MVC-Entwicklung für mittelgroße Anwendungen.































