10. L'applicazione [SimuPaie] – versione 6 – client ASP.NET di un servizio web
10.1. L'architettura dell'applicazione
Stiamo evolvendo l’architettura client/server del test NUnit nel modo seguente:
![]() |
In [1], il test NUnit viene sostituito dall’applicazione web della versione 8. È importante ricordare che questa architettura comprende due server web non rappresentati:
- un server web che esegue il servizio web [S]. Esso verrà eseguito in una prima istanza di Visual Web Developer.
- un server web che esegue il client web [1]. Esso verrà eseguito in una seconda istanza di Visual Web Developer.
10.2. Il progetto Visual Web Developer del client [web]
Creiamo un nuovo progetto Web ASP.NET:
![]() |
- in [1], scegliamo un progetto Web in C#
- in [2], scegliamo "Applicazione Web ASP.NET"
- in [3], assegniamo un nome al progetto web
- In [4], indichiamo una posizione per questo progetto
- in [5], il progetto creato
Modifichiamo alcune proprietà del progetto:
![]() |
- in [1], il nome dell'assembly che verrà generato
- in [2], lo spazio dei nomi predefinito delle classi e delle interfacce che verranno create
Per compilare il progetto [pam-v6-client-webservice] è possibile recuperare i file [Global.asax] e [Default.aspx] dal progetto web [pam-v4-3tier-nhibernate-multivues-monopage] e copiarli nel progetto [pam-v6-client-webservice]. Per farlo, si utilizza innanzitutto Esplora risorse di Windows:
![]() |
- in [1], la cartella del progetto [pam-v4-3tier-nhibernate-multivues-monopage]
- in [2], la cartella del progetto [pam-v6-client-webservice] dopo la ricopia
- delle cartelle [images, pam, ressources]
- dei file [Global.asax, Global.asax.cs]
- dei file [Default.aspx, Default.aspx.cs, Default.aspx.designer.cs]
- in [3], in Visual Studio Express, si visualizzano tutti i file del progetto, per far apparire i file appena aggiunti
- in [4], si includono nel progetto le cartelle e i file aggiunti
![]() |
- in [5] il nuovo progetto [pam-v6-client-webservice]
A questo punto, è possibile generare il progetto una prima volta [6]. Si ottengono i seguenti errori:
Per comprendere questi errori e correggerli, è necessario tornare all’architettura del progetto web in fase di realizzazione:
![]() |
Il progetto [pam-v6-client-webservice] corrisponde al livello [web] [1] nello schema sopra riportato. Si nota che questo livello comunica con il client [C] del servizio web, un client che genereremo a breve.
- Gli errori 2, 5 e 7 derivano dal fatto che il codice di [pam-v4] faceva riferimento a DLL del livello [metier], DLL, che ora si trova sul lato server e non sul lato client.
- Gli errori 1 e 3 hanno la stessa causa, ma questa volta riguardano il DLL del livello [dao]
- l’errore 4 deriva dal fatto che il codice di [Global.asax] utilizza Spring e il nostro progetto non fa riferimento a DLL di Spring. Aggiungeremo questo riferimento.
- L'errore 6 deriva dal fatto che la classe [Employe] è definita nel livello [dao], che non esiste sul lato client.
Gli errori relativi allo spazio dei nomi 1, 2, 4, 5, 6 e 7 saranno risolti dalla generazione del client C del servizio web. L’errore 3 viene risolto aggiungendo al progetto un riferimento a DLL di Spring. Possiamo procedere come segue:
![]() |
- in [1], si aggiunge un riferimento al progetto [pam-v6-client-webservice]
- in [4], è stato aggiunto un riferimento a DLL e [Spring.Core] dalla cartella [lib].
Dopo aver aggiunto questo riferimento su Spring, la generazione del progetto presenta un errore in meno. Tutti gli altri errori sono dovuti a righe di codice che fanno riferimento a oggetti dei livelli [metier] e [dao] che ora si trovano sul server. Vedremo che questi oggetti mancanti verranno generati nel client [C] del servizio web.
![]() |
Prima di generare il client [C] del servizio web, modificheremo lo spazio dei nomi delle diverse classi presenti. Attualmente si trovano nello spazio dei nomi [pam-v4]. Modifichiamo questo spazio dei nomi in [pam-v6]. I file interessati sono i seguenti: Default.aspx.cs, Default.aspx.designer.cs, Global.asax.cs. Ad esempio:
....
namespace pam_v6
{
public class Global : System.Web.HttpApplication
{
// --- dati statici dell'applicazione ---
public static Employe[] Employes;
public static IPamMetier PamMetier = null;
....
Riga 2: lo spazio dei nomi pam_v4 è stato sostituito dallo spazio dei nomi pam_v6.
Inoltre, è necessario modificare il markup dei file: Default.aspx e Global.asax:
![]() |
Il markup di [Default.aspx] diventa:
<%@ 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">
.....
Riga 1: l’attributo Inherits indica il nome della classe del file [Default.aspx.cs]. Si modifica lo spazio dei nomi.
Il markup di [Global.asax] diventa:
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v6.Global" Language="C#" %>
Nell'esempio sopra riportato, si modifica lo spazio dei nomi dell'attributo Inherits.
Ora generiamo il client [C] del servizio web.
![]() |
Per eseguire le operazioni successive, è necessario che il servizio web [S] [pam-v5-webservice] sia avviato in un'altra istanza di Visual Web Developer.
![]() |
- In [1], si aggiunge al progetto [pam-v6-client-webservice] un riferimento al servizio web [pam-v5-webservice]
- in [2], l'URI del servizio web [pam-v5-webservice]. Si tratta dell'URL del file WSDL di questo servizio web. Nella creazione del servizio web [pam-v5-webservice], è stato spiegato come individuare questo URL.
- In [3], si richiede all’assistente di elaborare il file WSDL indicato in [2]
- in [4], il servizio web [Service1Soap] individuato e in [5] i metodi remoti che esso espone.
- In [6], si imposta lo spazio dei nomi in cui verranno generate le classi e le interfacce del client [C]
- Si avvia la generazione del client [C]
![]() |
- in [1], il client generato. Fare doppio clic su di esso per accedere al suo contenuto.
- In [2], nell’esploratore degli oggetti, vengono visualizzate le classi e le interfacce dello spazio dei nomi pam_v6.WsPam. Si tratta dello spazio dei nomi del client generato.
- In [3], la classe che implementa il client del servizio web.
- In [4] sono riportati i metodi implementati dal client [Service1SoapClient]. Vi si trovano i due metodi del servizio web remoto [5] e [6].
- In [2] sono presenti le immagini delle entità dei livelli:
- [metier]: FeuilleSalaire, ElementsSalaire
- [dao]: Employe, Cotisations, Indemnites
Di seguito, è importante ricordare che queste immagini delle entità remote si trovano sul lato client e nello spazio dei nomi pam_v6.WsPam.
Esaminiamo i metodi e le proprietà esposti da una di esse:
![]() |
- in [1], si seleziona la classe locale [Employe]
- in [2], si trovano le proprietà dell’entità remota [Employe], nonché i campi privati utilizzati per le esigenze specifiche dell’entità locale.
Esaminiamo il codice di [Global.asax.cs] che presenta degli errori:
![]() |
- le righe 3 e 4 utilizzano spazi dei nomi presenti sul server ma non sul client.
- la riga 13 utilizza una classe [Employe] per la quale non è stato dichiarato il namespace corretto
- la riga 14 utilizza un'interfaccia IPamMetier sconosciuta sul client.
Abbiamo già riscontrato problemi simili nel client C# esaminato in precedenza.
Nell'architettura:
![]() |
- il livello locale [metier] è implementato dal client [C] di tipo pam_v6.WsPam.Service1SoapClient, che non implementa l’interfaccia IPamMetier, sebbene presenti metodi con gli stessi nomi.
- Le entità gestite (Employe, Indemnites, Cotisations) dal client [C] generato si trovano nello spazio dei nomi pam_v6.WsPam
Il codice di [Global.asax.cs] si evolve come segue:
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
{
// --- dati statici dell'applicazione ---
public static Employe[] Employes;
public static Service1SoapClient PamMetier = null;
public static string Msg;
public static bool Erreur = false;
// avvio dell'applicazione
public void Application_Start(object sender, EventArgs e)
{
// elaborazione del file di configurazione
try
{
// istanziazione del livello [metier]
PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
// elenco semplificato dei dipendenti
Employes = PamMetier.GetAllIdentitesEmployes();
// operazione riuscita
Msg = "Base chargée...";
}
catch (Exception ex)
{
// si registra l'errore
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)
{
// si inserisce un elenco di simulazioni vuoto nella sessione
List<Simulation> simulations = new List<Simulation>();
Session["simulations"] = simulations;
}
}
}
La riga 24 istanzia il livello [C] tramite Spring. La configurazione necessaria per questa istanziazione è definita 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>
Alla riga 16, il livello [metier] è un'istanza della classe [pam_v6.WsPam.Service1SoapClient] che si trova in DLL [pam-v6-client-webservice]. Ricordiamo che abbiamo configurato il progetto [pam-v6-client-webservice] in modo che generi questo DLL.
Nel file [Default.aspx.cs] sono ancora presenti alcuni errori:
![]() | ![]() |
- righe 5, 6: questi spazi dei nomi non esistono più. Le entità si trovano ora nello spazio dei nomi pam_v6 o pam_v6.WsPam.
- riga 98: il client generato [C] non ha ereditato la classe [PamException] dal livello [dao]. Non poteva farlo poiché il servizio web non espone tale eccezione. Si decide di sostituire PamException con la sua classe padre Exception.
Il codice diventa:
...
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)
{
...
Una volta corretti questi errori, possiamo eseguire l’applicazione web:
![]() |
- in [1], l'URL del client web del servizio web remoto
- in [2], il menu a tendina dei dipendenti è stato compilato. I dati in esso contenuti provengono dal servizio web.
Invitiamo il lettore a provare questa versione 6, copia della versione 4.

















