Skip to content

10. Aplikacja [SimuPaie] – wersja 6 – klient ASP.NET serwisu internetowego

10.1. Architektura aplikacji

Rozwijamy architekturę klient-serwer testu NUnit w następujący sposób:

W [1] test NUnit zostaje zastąpiony przez aplikację internetową w wersji 8. Należy pamiętać, że architektura ta obejmuje dwa serwery internetowe, które nie zostały przedstawione na rysunku:

  • serwer internetowy, na którym uruchomiona jest usługa internetowa [S]. Będzie ona działać w pierwszej instancji programu Visual Web Developer.
  • serwer internetowy, na którym uruchomiony jest klient internetowy [1]. Będzie on działał w drugiej instancji programu Visual Web Developer.

10.2. Projekt Visual Web Developer dla klienta [web]

Tworzymy nowy projekt internetowy o nazwie ASP.NET:

  • w [1] wybieramy projekt internetowy w języku C#
  • w [2] wybieramy „Aplikacja internetowa ASP.NET”
  • w [3] nadajemy nazwę projektowi internetowemu
  • W pliku [4] podajemy lokalizację tego projektu
  • w pliku [5] znajduje się utworzony projekt

Modyfikujemy niektóre właściwości projektu:

  • w pliku [1] – nazwa zestawu, który zostanie wygenerowany
  • w [2] – domyślna przestrzeń nazw klas i interfejsów, które zostaną utworzone

Aby skompilować projekt [pam-v6-client-webservice], można pobrać pliki [Global.asax] i [Default.aspx] z projektu internetowego [pam-v4-3tier-nhibernate-multivues-monopage] i skopiować je do projektu [pam-v6-client-webservice]. Najpierw wykonujemy to za pomocą Eksploratora Windows:

  • do [1], folder projektu [pam-v4-3tier-nhibernate-multivues-monopage]
  • do [2], a folder projektu [pam-v6-client-webservice] po skopiowaniu
    • folderów [images, pam, ressources]
    • plików [Global.asax, Global.asax.cs]
    • plików [Default.aspx, Default.aspx.cs, Default.aspx.designer.cs]
  • do [3], w programie Visual Studio Express wyświetla się wszystkie pliki projektu, aby pokazać nowo dodane pliki
  • w pliku [4] należy dołączyć do projektu dodane foldery i pliki
  • w [5] nowy projekt [pam-v6-client-webservice]

Na tym etapie można po raz pierwszy wygenerować projekt [6]. Pojawiają się następujące błędy:

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

Aby zrozumieć te błędy i je skorygować, należy powrócić do architektury tworzonego projektu internetowego:

Projekt [pam-v6-client-webservice] stanowi warstwę [web] [1] na powyższym schemacie. Widać, że warstwa ta komunikuje się z klientem serwisu internetowego o nazwie [C], którego wkrótce wygenerujemy.

  • Błędy 2, 5 i 7 wynikają z faktu, że kod warstwy [pam-v4] odwoływał się do warstwy DLL należącej do warstwy [metier], DLL, który obecnie znajduje się po stronie serwera, a nie po stronie klienta.
  • Błędy 1 i 3 mają tę samą przyczynę, ale tym razem dotyczą warstwy DLL z warstwy [dao]
  • błąd 4 wynika z faktu, że kod klasy [Global.asax] wykorzystuje Spring, a nasz projekt nie odwołuje się do klasy DLL z biblioteki Spring. Dodamy to odwołanie.
  • Błąd 6 wynika z faktu, że klasa [Employe] jest zdefiniowana w warstwie [dao], która nie istnieje po stronie klienta.

Błędy przestrzeni nazw 1, 2, 4, 5, 6 i 7 zostaną rozwiązane poprzez wygenerowanie klienta C dla usługi internetowej. Błąd 3 rozwiązuje się poprzez dodanie do projektu odwołania do klasy DLL biblioteki Spring. Możemy postępować w następujący sposób:

  • w [1] dodajemy odwołanie do projektu [pam-v6-client-webservice]
  • w pliku [4] dodano odwołanie do pliku DLL oraz [Spring.Core] z folderu [lib].

Po dodaniu tego odwołania w Springu podczas generowania projektu pojawia się o jeden błąd mniej. Wszystkie pozostałe błędy wynikają z linii kodu odwołujących się do obiektów z warstw [metier] i [dao], które znajdują się obecnie na serwerze. Zobaczymy, że te brakujące obiekty zostaną wygenerowane w kliencie [C] usługi internetowej.

Przed wygenerowaniem klienta serwisu internetowego o nazwie [C] zmodyfikujemy przestrzeń nazw poszczególnych klas. Obecnie znajdują się one w przestrzeni nazw [pam-v4]. Zmieniamy tę przestrzeń nazw na [pam-v6]. Dotyczy to następujących plików: Default.aspx.cs, Default.aspx.designer.cs, Global.asax.cs. Na przykład:


....
namespace pam_v6
{
  public class Global : System.Web.HttpApplication
  {
    // --- dane statyczne aplikacji ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;
....

W wierszu 2 przestrzeń nazw pam_v4 została zastąpiona przestrzenią nazw pam_v6.

Ponadto należy zmodyfikować znaczniki w plikach: Default.aspx i Global.asax:

  

Znaczniki pliku [Default.aspx] otrzymują następującą postać:


<%@ 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">
.....

W wierszu 1 atrybut Inherits określa nazwę klasy pliku [Default.aspx.cs]. Zmienia się przestrzeń nazw.

Znaczniki pliku [Global.asax] zmieniają się na:


<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v6.Global" Language="C#" %>

Powyżej zmieniono przestrzeń nazw atrybutu Inherits.

Teraz generujemy klienta [C] usługi internetowej.

Aby wykonać poniższe czynności, usługa internetowa [S] [pam-v5-webservice] musi być uruchomiona w innej instancji programu Visual Web Developer.

  • W [1] dodajemy do projektu [pam-v6-client-webservice] odwołanie do usługi internetowej [pam-v5-webservice]
  • w pliku [2], URI usługi internetowej [pam-v5-webservice]. Jest to URL pliku WSDL tej usługi internetowej. W opisie serwisu internetowego [pam-v5-webservice] wyjaśniono, jak uzyskać ten adres URL.
  • W [3] prosi się asystenta o przetworzenie pliku WSDL wskazanego w [2]
  • w pliku [4] przedstawiono wykrytą usługę internetową [Service1Soap], a w pliku [5] – udostępniane przez nią metody zdalne.
  • w pliku [6] określa się przestrzeń nazw, w której zostaną wygenerowane klasy i interfejsy klienta [C]
  • uruchamia się generowanie klienta [C]
  • w [1] znajduje się wygenerowany klient. Klikamy na niego dwukrotnie, aby uzyskać dostęp do jego zawartości.
  • W [2], w eksploratorze obiektów, wyświetlane są klasy i interfejsy przestrzeni nazw pam_v6.WsPam. Jest to przestrzeń nazw wygenerowanego klienta.
  • W pliku [3] znajduje się klasa implementująca klienta serwisu internetowego.
  • W [4] znajdują się metody zaimplementowane przez klienta [Service1SoapClient]. Znajdują się tam dwie metody zdalnej usługi internetowej: [5] i [6].
  • W pliku [2] znajdują się obrazy obiektów z następujących warstw:
    • [metier]: FeuilleSalaire, ElementsSalaire
    • [dao]: Employe, Cotisations, Indemnites

W dalszej części należy pamiętać, że te obrazy zdalnych obiektów znajdują się po stronie klienta oraz w przestrzeni nazw pam_v6.WsPam.

Przyjrzyjmy się metodom i właściwościom udostępnianym przez jeden z nich:

  • w [1] wybieramy lokalną klasę [Employe]
  • w [2] znajdują się właściwości zdalnej encji [Employe], a także pola prywatne wykorzystywane na potrzeby samej encji lokalnej.

Przyjrzyjmy się kodowi klasy [Global.asax.cs], w którym występują błędy:

 
  • w wierszach 3 i 4 użyto przestrzeni nazw, które istnieją na serwerze, ale nie u klienta.
  • w wierszu 13 użyto klasy [Employe], dla której nie zadeklarowano właściwej przestrzeni nazw
  • w wierszu 14 użyto interfejsu IPamMetier, który jest nieznany po stronie klienta.

Podobne problemy napotkaliśmy już wcześniej w analizowanym kliencie C#.

W architekturze:

  • lokalna warstwa [metier] jest zaimplementowana przez klienta [C] typu pam_v6.WsPam.Service1SoapClient, który nie implementuje interfejsu IPamMetier, mimo że posiada metody o tych samych nazwach.
  • Obsługiwane encje (Employe, Indemnites, Cotisations) przez wygenerowanego klienta [C] znajdują się w przestrzeni nazw pam_v6.WsPam

Kod [Global.asax.cs] zmienia się w następujący sposób:


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
  {
    // --- dane statyczne aplikacji ---
    public static Employe[] Employes;
    public static Service1SoapClient PamMetier = null;
    public static string Msg;
    public static bool Erreur = false;

    // uruchomienie aplikacji
    public void Application_Start(object sender, EventArgs e)
    {
      // przetwarzanie pliku konfiguracyjnego
      try
      {
        // utworzenie instancji warstwy [metier]
        PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
        // uproszczona lista pracowników
        Employes = PamMetier.GetAllIdentitesEmployes();
        // operacja zakończyła się powodzeniem
        Msg = "Base chargée...";
      }
      catch (Exception ex)
      {
        // odnotowano błąd
        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)
    {
      // do sesji dodano pustą listę symulacji 
      List<Simulation> simulations = new List<Simulation>();
      Session["simulations"] = simulations;
    }
  }
}

W wierszu 24 za pomocą Springa tworzona jest instancja warstwy [C]. Konfiguracja niezbędna do tej instancji znajduje się w pliku [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>

W wierszu 16 warstwa [metier] jest instancją klasy [pam_v6.WsPam.Service1SoapClient], którą można znaleźć w DLL [pam-v6-client-webservice]. Przypominamy, że skonfigurowaliśmy projekt [pam-v6-client-webservice] tak, aby generował ten plik DLL.

W pliku [Default.aspx.cs] pozostało jeszcze kilka błędów:

  • wiersze 5, 6: te przestrzenie nazw już nie istnieją. Entities znajdują się teraz w przestrzeni nazw pam_v6 lub pam_v6.WsPam.
  • wiersz 98: wygenerowany klient [C] nie przejął klasy [PamException] z warstwy [dao]. Nie mógł tego zrobić, ponieważ usługa internetowa nie udostępnia tego wyjątku. Postanowiono zastąpić klasę PamException jej klasą nadrzędną Exception.

Kod przyjmuje następującą postać:


...
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)
      {
...

Po skorygowaniu tych błędów możemy uruchomić aplikację internetową:

  • w [1], URL klienta internetowego zdalnej usługi internetowej
  • w [2], formularz pracowników został wypełniony. Dane w nim zawarte pochodzą z serwisu internetowego.

Zachęcamy czytelnika do przetestowania tej wersji 6, będącej kopią wersji 4.