Skip to content

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:

1
2
3
4
5
6
7
Erreur    1    Le type ou le nom d'espace de noms 'Dao' n'existe pas dans l'espace de noms 'Pam' (une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    3    11    pam-v6-client-webservice
Erreur    2    Le type ou le nom d'espace de noms 'Metier' n'existe pas dans l'espace de noms 'Pam' (une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    4    11    pam-v6-client-webservice
Erreur    3    Le type ou le nom d'espace de noms 'Dao' n'existe pas dans l'espace de noms 'Pam' (une référence d'assembly est-elle manquante ?)    c:\temp\pam-aspnet\pam-v6-client-webservice\default.aspx.cs    5    11    pam-v6-client-webservice
Erreur    4    Le type ou le nom d'espace de noms 'Spring' est introuvable (une directive using ou une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    6    7    pam-v6-client-webservice
Erreur    5    Le type ou le nom d'espace de noms 'Metier' n'existe pas dans l'espace de noms 'Pam' (une référence d'assembly est-elle manquante ?)    c:\temp\pam-aspnet\pam-v6-client-webservice\default.aspx.cs    6    11    pam-v6-client-webservice
Erreur    6    Le type ou le nom d'espace de noms 'Employe' est introuvable (une directive using ou une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    13    19    pam-v6-client-webservice
Erreur    7    Le type ou le nom d'espace de noms 'IPamMetier' est introuvable (une directive using ou une référence d'assembly est-elle manquante ?)    C:\temp\pam-aspnet\pam-v6-client-webservice\Global.asax.cs    14    19    pam-v6-client-webservice

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.