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

















