10. Die Anwendung [SimuPaie] – Version 6 – Client ASP.NET eines Webdienstes
10.1. Die Architektur der Anwendung
Wir entwickeln die Client-Server-Architektur des Tests NUnit wie folgt weiter:
![]() |
In [1] wird der Test NUnit durch die Webanwendung der Version 8 ersetzt. Dabei ist zu beachten, dass diese Architektur zwei nicht dargestellte Webserver umfasst:
- einen Webserver, auf dem der Webdienst [S] ausgeführt wird. Dieser läuft in einer ersten Instanz von Visual Web Developer.
- ein Webserver, auf dem der Webclient [1] ausgeführt wird. Dieser wird in einer zweiten Instanz von Visual Web Developer ausgeführt.
10.2. Das Visual Web Developer-Projekt des Clients [web]
Wir erstellen ein neues Webprojekt ASP.NET:
![]() |
- In [1] wählen wir ein Webprojekt in C#
- in [2] wählen wir „Webanwendung ASP.NET“
- In [3] geben wir dem Webprojekt einen Namen
- In [4] geben wir einen Speicherort für dieses Projekt an
- In [5] wird das Projekt erstellt
Wir ändern einige Eigenschaften des Projekts:
![]() |
- in [1] den Namen der zu generierenden Assembly
- in [2], der Standard-Namespace für die zu erstellenden Klassen und Schnittstellen
Um das Projekt [pam-v6-client-webservice] zu erstellen, können wir die Dateien [Global.asax] und [Default.aspx] aus dem Webprojekt [pam-v4-3tier-nhibernate-multivues-monopage] übernehmen und in das Projekt [pam-v6-client-webservice] kopieren. Dies erfolgt zunächst über den Windows-Explorer:
![]() |
- in [1], den Projektordner [pam-v4-3tier-nhibernate-multivues-monopage]
- in [2], den Projektordner [pam-v6-client-webservice] nach dem Kopieren
- der Ordner [images, pam, ressources]
- der Dateien [Global.asax, Global.asax.cs]
- der Dateien [Default.aspx, Default.aspx.cs, Default.aspx.designer.cs]
- in [3]. In Visual Studio Express lassen wir alle Projektdateien anzeigen, um die neu hinzugefügten Dateien
- in [4] werden die hinzugefügten Ordner und Dateien in das Projekt aufgenommen
![]() |
- in [5] das neue Projekt [pam-v6-client-webservice]
An dieser Stelle kann das Projekt ein erstes Mal generiert werden: [6]. Dabei treten folgende Fehler auf:
Um diese Fehler zu verstehen und zu beheben, muss man sich die Architektur des im Aufbau befindlichen Webprojekts noch einmal ansehen:
![]() |
Das Projekt [pam-v6-client-webservice] entspricht der Schicht [web] [1] im obigen Schema. Man sieht, dass diese Schicht mit dem Webservice-Client [C] kommuniziert, einem Client, den wir in Kürze generieren werden.
- Die Fehler 2, 5 und 7 sind darauf zurückzuführen, dass der Code von [pam-v4] auf DLL der Schicht [metier] verwies, DLL, die sich nun auf der Serverseite und nicht mehr auf der Clientseite befindet.
- Die Fehler 1 und 3 haben dieselbe Ursache, betreffen diesmal jedoch die DLL der Schicht [dao]
- Fehler 4 rührt daher, dass der Code von [Global.asax] Spring verwendet und unser Projekt nicht auf die Spring-Klasse DLL verweist. Wir werden diesen Verweis hinzufügen.
- Fehler 6 rührt daher, dass die Klasse [Employe] in der Schicht [dao] definiert ist, die auf der Client-Seite nicht existiert.
Die Namespace-Fehler 1, 2, 4, 5, 6 und 7 werden durch die Generierung des C-Clients für den Webdienst behoben. Fehler 3 wird behoben, indem dem Projekt ein Verweis auf die Spring-Klasse „DLL“ hinzugefügt wird. Wir können wie folgt vorgehen:
![]() |
- in [1] fügen wir einen Verweis auf das Projekt [pam-v6-client-webservice] hinzu
- in [4] wurde ein Verweis auf DLL und [Spring.Core] aus dem Ordner [lib] hinzugefügt.
Nach dem Hinzufügen dieses Verweises in Spring weist die Projektgenerierung einen Fehler weniger auf. Alle anderen Fehler sind auf Codezeilen zurückzuführen, die auf Objekte der Schichten [metier] und [dao] verweisen, die sich nun auf dem Server befinden. Wir werden sehen, dass diese fehlenden Objekte im Webservice-Client [C] generiert werden.
![]() |
Bevor wir den Webdienst-Client [C] generieren, werden wir den Namensraum der verschiedenen vorhandenen Klassen ändern. Diese befinden sich derzeit im Namensraum [pam-v4]. Wir ändern diesen Namensraum in [pam-v6]. Betroffen sind folgende Dateien: Default.aspx.cs, Default.aspx.designer.cs, Global.asax.cs. Beispiel:
....
namespace pam_v6
{
public class Global : System.Web.HttpApplication
{
// --- statische Anwendungsdaten ---
public static Employe[] Employes;
public static IPamMetier PamMetier = null;
....
In Zeile 2 wurde der Namensraum „pam_v4“ durch den Namensraum „pam_v6“ ersetzt.
Außerdem muss das Markup der Dateien Default.aspx und Global.asax geändert werden:
![]() |
Die Markierung von [Default.aspx] lautet nun:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="pam_v6.PagePam" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
.....
In Zeile 1 bezeichnet das Attribut Inherits den Klassennamen der Datei [Default.aspx.cs]. Der Namensraum wird geändert.
Der Markup-Code von [Global.asax] lautet nun:
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v6.Global" Language="C#" %>
Oben wird der Namensraum des Attributs Inherits geändert.
Nun generieren wir den Webservice-Client [C].
![]() |
Um die folgenden Schritte auszuführen, muss der Webdienst [S] [pam-v5-webservice] in einer anderen Instanz von Visual Web Developer gestartet werden.
![]() |
- In [1] wird dem Projekt [pam-v6-client-webservice] eine Referenz auf den Webdienst [pam-v5-webservice]
- in [2] wird der URI des Webdienstes [pam-v5-webservice] hinzugefügt. Dies ist der URL der Datei WSDL dieses Webdienstes. In der Beschreibung des Webdienstes [pam-v5-webservice] wurde erläutert, wie man diese URL ermittelt.
- In [3] wird der Assistent aufgefordert, die in [2] angegebene Datei WSDL
- In [4] werden der Webdienst [Service1Soap] und in [5] die von ihm bereitgestellten Remote-Methoden ermittelt.
- In [6] wird der Namensraum festgelegt, in dem die Klassen und Schnittstellen des Clients [C] generiert werden sollen
- Die Generierung des Clients [C] wird gestartet
![]() |
- in [1], der generierte Client. Durch Doppelklick darauf erhält man Zugriff auf dessen Inhalt.
- In [2] werden im Objekt-Explorer die Klassen und Schnittstellen des Namensraums pam_v6.WsPam angezeigt. Dies ist der Namensraum des generierten Clients.
- In [3] wird die Klasse angezeigt, die den Webservice-Client implementiert.
- In [4] werden die vom Client [Service1SoapClient] implementierten Methoden angezeigt. Dort finden sich die beiden Methoden des Remote-Webdienstes [5] und [6].
- In [2] sind die Bilder der Objekte der Ebenen enthalten:
- [metier]: FeuilleSalaire, ElementsSalaire
- [dao]: Employe, Cotisations, Indemnites
Im weiteren Verlauf ist zu beachten, dass sich diese Abbildungen der entfernten Entitäten auf der Client-Seite und im Namensraum pam_v6.WsPam befinden.
Betrachten wir die von einem dieser Objekte bereitgestellten Methoden und Eigenschaften:
![]() |
- In [1] wird die lokale Klasse [Employe] ausgewählt
- in [2] finden wir die Eigenschaften der entfernten Entität [Employe] sowie private Felder, die für die eigenen Zwecke der lokalen Entität verwendet werden.
Sehen wir uns den Code von [Global.asax.cs] an, der Fehler enthält:
![]() |
- In den Zeilen 3 und 4 werden Namespaces verwendet, die auf dem Server, aber nicht auf dem Client vorhanden sind.
- In Zeile 13 wird eine Klasse „[Employe]“ verwendet, für die der richtige Namespace nicht deklariert wurde.
- In Zeile 14 wird eine Schnittstelle „IPamMetier“ verwendet, die auf dem Client unbekannt ist.
Ähnliche Probleme sind uns bereits in dem zuvor untersuchten C#-Client begegnet.
In der Architektur:
![]() |
- wird die lokale Schicht [metier] durch den Client [C] vom Typ pam_v6.WsPam.Service1SoapClient implementiert, der die Schnittstelle IPamMetier nicht implementiert, obwohl er ansonsten Methoden mit denselben Namen aufweist.
- Die bearbeiteten Entitäten (Employe, Indemnites, Cotisations) durch den generierten Client [C] befinden sich im Namensraum pam_v6.WsPam
Der Code von [Global.asax.cs] entwickelt sich wie folgt:
using System;
using System.Collections.Generic;
using Pam.Web;
using Spring.Context.Support;
using pam_v6.WsPam;
namespace pam_v6
{
public class Global : System.Web.HttpApplication
{
// --- statische Anwendungsdaten ---
public static Employe[] Employes;
public static Service1SoapClient PamMetier = null;
public static string Msg;
public static bool Erreur = false;
// Start der Anwendung
public void Application_Start(object sender, EventArgs e)
{
// Auswertung der Konfigurationsdatei
try
{
// Instanziierung der Schicht [metier]
PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
// vereinfachte Mitarbeiterliste
Employes = PamMetier.GetAllIdentitesEmployes();
// Es ist gelungen
Msg = "Base chargée...";
}
catch (Exception ex)
{
// Fehler wird vermerkt
Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
Erreur = true;
}
}
public void Session_Start(object sender, EventArgs e)
{
// Es wird eine leere Simulationsliste in die Sitzung eingefügt
List<Simulation> simulations = new List<Simulation>();
Session["simulations"] = simulations;
}
}
}
In Zeile 24 wird die Schicht [C] mithilfe von Spring instanziiert. Die für diese Instanziierung erforderliche Konfiguration erfolgt in [web.config]:
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
...
</sectionGroup>
<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>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="pammetier" type="pam_v6.WsPam.Service1SoapClient,pam-v6-client-webservice"/>
</objects>
</spring>
In Zeile 16 ist die Schicht [metier] eine Instanz der Klasse [pam_v6.WsPam.Service1SoapClient], die in DLL und [pam-v6-client-webservice] zu finden ist. Zur Erinnerung: Wir haben das Projekt [pam-v6-client-webservice] so konfiguriert, dass es diese DLL generiert.
In [Default.aspx.cs] sind noch einige Fehler vorhanden:
![]() | ![]() |
- Zeilen 5, 6: Diese Namensräume existieren nicht mehr. Die Entitäten befinden sich nun im Namensraum pam_v6 oder pam_v6.WsPam.
- Zeile 98: Der generierte Client [C] hat die Klasse [PamException] aus der Schicht [dao] nicht übernommen. Dies war nicht möglich, da der Webdienst diese Ausnahme nicht bereitstellt. Es wird beschlossen, PamException durch seine übergeordnete Klasse Exception zu ersetzen.
Der Code lautet nun:
...
using System.Web.UI.WebControls;
using pam_v6.WsPam;
namespace pam_v6
{
public partial class PagePam : Page
{
...
...
try
{
feuillesalaire = Global.PamMetier.GetSalaire(DropDownListEmployes.SelectedValue, HeuresTravaillées, JoursTravaillés);
}
catch (Exception ex)
{
...
Nachdem diese Fehler behoben wurden, können wir die Webanwendung ausführen:
![]() |
- in [1], dem URL des Web-Clients des Remote-Webdienstes
- in [2] wurde das Mitarbeiter-Kombinationsfeld ausgefüllt. Die darin enthaltenen Daten stammen vom Webdienst.
Wir laden den Leser ein, diese Version 6 – eine Kopie der Version 4 – zu testen.

















