10. L'applicazione [SimuPaie] – versione 6 – un client ASP.NET di un servizio web
10.1. L'architettura dell'applicazione
Stiamo evolvendo l'architettura client/server del test NUnit come segue:
![]() |
In [1], il test NUnit viene sostituito dall'applicazione web versione 8. È importante notare che questa architettura include due server web non mostrati:
- un server web che esegue il servizio web [S]. Verrà eseguito in una prima istanza di Visual Web Developer.
- un server web che esegue il client web [1]. Verrà eseguito in una seconda istanza di Visual Web Developer.
10.2. Il progetto Visual Web Developer per il client [web]
Creiamo un nuovo progetto Web ASP.NET:
![]() |
- in [1], selezioniamo un progetto Web C#
- In [2], selezioniamo "Applicazione Web ASP.NET"
- in [3], assegniamo un nome al progetto web
- in [4], specifichiamo una posizione per questo progetto
- in [5], il progetto viene creato
Modifichiamo alcune proprietà del progetto:
![]() |
- in [1], il nome dell'assembly che verrà generato
- In [2], lo spazio dei nomi predefinito per le classi e le interfacce che verranno create
Per compilare il progetto [pam-v6-client-webservice], possiamo 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, utilizziamo innanzitutto Esplora risorse:
![]() |
- in [1], la cartella del progetto [pam-v4-3tier-nhibernate-multivues-monopage]
- in [2], la cartella del progetto [pam-v6-client-webservice] dopo la copia
- le cartelle [images, pam, resources]
- i file [Global.asax, Global.asax.cs]
- i file [Default.aspx, Default.aspx.cs, Default.aspx.designer.cs]
- in [3], in Visual Studio Express, visualizza tutti i file di progetto per mostrare i file appena aggiunti
- In [4], includere le cartelle e i file aggiunti nel progetto
![]() |
- in [5], il nuovo progetto [pam-v6-client-webservice]
A questo punto, è possibile compilare il progetto per la prima volta [6]. Verranno visualizzati i seguenti errori:
Per comprendere e correggere questi errori, dobbiamo esaminare l'architettura del progetto web in fase di sviluppo:
![]() |
Il progetto [pam-v6-client-webservice] è il livello [web] [1] nel diagramma sopra riportato. Possiamo notare che questo livello comunica con il client del servizio web [C], un client che genereremo tra poco.
- Gli errori 2, 5 e 7 derivano dal fatto che il codice in [pam-v4] faceva riferimento alla DLL del livello [business], una DLL che ora si trova sul lato server anziché sul lato client.
- Gli errori 1 e 3 hanno la stessa causa, ma questa volta riguardano la DLL del livello [DAO].
- L'errore 4 è causato dal fatto che il codice in [Global.asax] utilizza Spring, e il nostro progetto non fa riferimento alla DLL di Spring. Aggiungeremo questo riferimento.
- L'errore 6 è causato dal fatto che la classe [Employee] è definita nel livello [DAO], che non esiste sul lato client.
Gli errori di namespace 1, 2, 4, 5, 6 e 7 saranno risolti generando il client C per il servizio web. L'errore 3 viene risolto aggiungendo un riferimento alla DLL di Spring nel progetto. Possiamo procedere come segue:
![]() |
- In [1], aggiungere un riferimento al progetto [pam-v6-client-webservice]
- In [4], abbiamo aggiunto un riferimento alla DLL [Spring.Core] nella cartella [lib].
Dopo aver aggiunto questo riferimento a Spring, la generazione del progetto mostra un errore in meno. Tutti gli altri errori sono dovuti a righe di codice che fanno riferimento a oggetti dei livelli [business] e [DAO], che ora si trovano sul server. Vedremo che questi oggetti mancanti verranno generati nel client del servizio web [C].
![]() |
Prima di generare il client del servizio web [C], modificheremo lo spazio dei nomi delle varie 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
{
// --- static application data ---
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 nei seguenti file: Default.aspx e Global.asax:
![]() |
Il codice di marcatura per [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 specifica il nome della classe del file [Default.aspx.cs]. Modifichiamo lo spazio dei nomi.
Il markup per [Global.asax] diventa:
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v6.Global" Language="C#" %>
Sopra, modifichiamo lo spazio dei nomi dell'attributo Inherits.
Ora generiamo il client del servizio web [C].
![]() |
Per eseguire i passaggi seguenti, il servizio Web [S] [pam-v5-webservice] deve essere in esecuzione in un'altra istanza di Visual Web Developer.
![]() |
- In [1], aggiungiamo un riferimento al servizio web [pam-v5-webservice] al progetto [pam-v6-client-webservice]
- In [2], inserisci l'URI del servizio web [pam-v5-webservice]. Si tratta dell'URL del file WSDL per questo servizio web. Durante la creazione del servizio web [pam-v5-webservice], abbiamo indicato come trovare questo URL.
- In [3], chiedi alla procedura guidata di utilizzare il file WSDL specificato in [2]
- in [4], il servizio web individuato [Service1Soap] e in [5] i metodi remoti che espone.
- In [6], impostiamo lo spazio dei nomi in cui verranno generate le classi e le interfacce del client [C]
- Iniziamo a generare il client [C]
![]() |
- In [1], il client generato. Fare doppio clic su di esso per accedere al suo contenuto.
- In [2], in Esplora oggetti, vengono visualizzate le classi e le interfacce dello spazio dei nomi pam_v6.WsPam. Questo è lo spazio dei nomi del client generato.
- [3] mostra la classe che implementa il client del servizio web.
- In [4], i metodi implementati dal client [Service1SoapClient]. Qui troviamo i due metodi del servizio web remoto [5] e [6].
- In [2] sono riportati i diagrammi delle entità relativi ai livelli:
- [business] : Payroll, PayrollItems
- [DAO]: Dipendente, Contributi, Indennità
Proseguendo, tieni presente che queste rappresentazioni 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], selezioniamo la classe locale [Employee]
- In [2], vediamo le proprietà dell'entità remota [Employee] e i campi privati utilizzati per le esigenze specifiche dell'entità locale.
Esaminiamo il codice in [Global.asax.cs] che contiene errori:
![]() |
- Le righe 3 e 4 utilizzano spazi dei nomi presenti sul server ma non sul client.
- La riga 13 utilizza una classe [Employee] per la quale non è stato dichiarato il namespace corretto
- La riga 14 utilizza un'interfaccia IPamMetier sconosciuta al client.
Abbiamo già riscontrato problemi simili nel client C# discusso in precedenza.
Nell'architettura:
![]() |
- il livello [aziendale] locale è implementato dal client [C] di tipo pam_v6.WsPam.Service1SoapClient, che non implementa l'interfaccia IPamMetier anche se dispone di metodi con gli stessi nomi.
- Le entità gestite (Employee, Benefits, Contributions) dal client [C] generato si trovano nello spazio dei nomi pam_v6.WsPam
Il codice in [Global.asax.cs] cambia 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
{
// --- static application data ---
public static Employe[] Employes;
public static Service1SoapClient PamMetier = null;
public static string Msg;
public static bool Erreur = false;
// application startup
public void Application_Start(object sender, EventArgs e)
{
// using the configuration file
try
{
// instantiation layer [metier]
PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
// simplified list of employees
Employes = PamMetier.GetAllIdentitesEmployes();
// we succeeded
Msg = "Base chargée...";
}
catch (Exception ex)
{
// we note the error
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)
{
// put an empty simulation list in the session
List<Simulation> simulations = new List<Simulation>();
Session["simulations"] = simulations;
}
}
}
La riga 24 istanzia il livello [C] utilizzando 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>
Riga 16: Il livello [business] è un'istanza della classe [pam_v6.WsPam.Service1SoapClient], che si trova nella DLL [pam-v6-client-webservice]. Ricordiamo che abbiamo configurato il progetto [pam-v6-client-webservice] per generare questa DLL.
Ci sono ancora alcuni errori in [Default.aspx.cs]:
![]() | ![]() |
- 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 ha potuto farlo poiché il servizio web non espone questa eccezione. Abbiamo scelto 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 per il servizio web remoto
- in [2], il menu a tendina dei dipendenti è stato popolato. I dati in esso contenuti provengono dal servizio web.
Invitiamo il lettore a provare questa versione 6, una copia della versione 4.

















