7. L'applicazione [SimuPaie] – versione 3 – architettura a 3 livelli con NHibernate
Letture consigliate: «Linguaggio C# 2008, Capitolo 4: Architetture a 3 livelli, test NUnit, framework Spring».
7.1. Architettura generale dell’applicazione
L'applicazione [SimuPaie] avrà ora la seguente struttura a tre livelli:
![]() |
- il livello [1-dao] (DAO = Data Access Object) si occuperà dell’accesso ai dati.
- Il livello [2-métier] si occuperà dell’aspetto business dell’applicazione, ovvero il calcolo delle retribuzioni.
- Il livello [3-ui] (ui=User Interface) si occuperà della presentazione dei dati all’utente e dell’esecuzione delle sue richieste. Chiamiamo [Application] l’insieme dei moduli che assicurano questa funzione. Esso funge da interfaccia con l’utente.
- I tre livelli saranno resi indipendenti grazie all’utilizzo di interfacce .NET
- L’integrazione dei diversi livelli sarà realizzata da Spring IoC
L’elaborazione di una richiesta da parte di un cliente si svolge secondo le seguenti fasi:
- il cliente invia una richiesta all’applicazione.
- L’applicazione elabora tale richiesta. A tal fine, potrebbe aver bisogno dell’aiuto del livello [métier], il quale a sua volta potrebbe aver bisogno del livello [dao] se è necessario scambiare dati con il database.
- L’applicazione riceve una risposta dal livello [métier]. In base a tale risposta, invia al cliente la vista (= la risposta) appropriata.
Prendiamo l’esempio del calcolo della retribuzione di un’assistente all’infanzia. Ciò richiederà diverse fasi:
![]() |
- il livello [ui] dovrà chiedere all’utente
- l’identità della persona di cui si desidera calcolare la busta paga
- il numero di giorni lavorati da parte di quest’ultima
- il numero di ore lavorate
- A tal fine, dovrà presentare all'utente l'elenco delle persone (cognome, nome, SS) presenti nella tabella [EMPLOYES] affinché l'utente ne scelga una. Il livello [ui] utilizzerà il percorso [2, 3, 4, 5, 6, 7] per ottenerli. L’operazione [2] è la richiesta dell’elenco dei dipendenti, l’operazione [7] è la risposta a tale richiesta. Fatto ciò, il livello [ui] può presentare l’elenco dei dipendenti all’utente tramite [8].
- L’utente trasmetterà al livello [ui] il numero di giorni lavorati e il numero di ore lavorate. Si tratta dell’operazione [1] sopra indicata. Durante questa fase, l’utente interagisce esclusivamente con il livello [ui]. È proprio questo livello che verificherà, in particolare, la validità dei dati inseriti. Una volta fatto ciò, l’utente richiederà il calcolo della busta paga.
- Il livello [ui] richiederà al livello di business di effettuare tale calcolo. A tal fine, gli trasmetterà i dati ricevuti dall’utente. Si tratta dell’operazione [2].
- Il livello [metier] necessita di alcune informazioni per portare a termine il proprio lavoro:
- informazioni più complete sulla persona (indirizzo, indice, ...)
- le indennità relative al suo indice
- le aliquote dei vari contributi sociali da prelevare dallo stipendio lordo
Richiederà queste informazioni al livello [dao] tramite il percorso [3, 4, 5, 6]. [3] è la richiesta iniziale e [6] la risposta a tale richiesta.
- Avendo tutti i dati di cui aveva bisogno, il livello [metier] calcola la busta paga della persona selezionata dall’utente.
- Il livello [metier] può ora rispondere alla richiesta del livello [ui] effettuata al punto (d). Si tratta del percorso [7].
- Il livello [ui] formatterà questi risultati per presentarli all'utente in una forma adeguata e poi visualizzarli. Si tratta del percorso [8].
- È possibile che questi risultati debbano essere memorizzati in un file o in un database. Ciò può avvenire in modo automatico. In tal caso, dopo l’operazione (f), il livello [metier] richiederà al livello [dao] di salvare i risultati. Il percorso sarà [3, 4, 5, 6]. Ciò può avvenire anche su richiesta dell’utente. Il percorso [1-8] sarà utilizzato dal ciclo richiesta-risposta.
Da questa descrizione si evince che un livello è portato a utilizzare le risorse del livello che si trova alla sua destra, mai di quello alla sua sinistra.
La nostra prima implementazione di questa architettura a 3 livelli sarà un’applicazione ASP.NET in cui
- i livelli [dao] e [metier] saranno implementati da DLL
- il livello [ui] sarà implementato dal modulo web della versione 1 (cfr. paragrafo 4.2.1).
Iniziamo implementando il livello [dao] con il framework NHibernate.
7.2. Il livello [dao] di accesso ai dati
![]() |
7.2.1. Il progetto Visual Studio C# del livello [dao]
Il progetto Visual Studio del livello [dao] è il seguente:
![]() |
- in [1], il progetto nella sua interezza
- in [2], le diverse classi del progetto
- in [3], i riferimenti del progetto.
- in [4], una cartella [lib] in cui sono stati raccolti i DLL necessari per i vari progetti che seguiranno
Nei riferimenti [3] del progetto si trovano i seguenti DLL:
- NHibernate: per il ORM NHibernate
- MySql.Data: il driver ADO.NET del SGBD MySQL
- Spring.Core: per il framework Spring
- log4net: una libreria di log
- nunit.framework: una libreria di test unitari
Questi riferimenti sono stati prelevati dalla cartella [lib] [4]. Si dovrà assicurarsi che per tutti questi riferimenti la proprietà “Copia locale” sia impostata su “True” [5]:
![]() |
7.2.2. Le entità del livello [dao]
![]() |
Le entità (oggetti) necessarie per il livello [dao] sono state raccolte nella cartella [entites] del progetto. Alcune ci sono già note: [Cotisations] descritta al paragrafo 6.3.2.1, [Employe] descritta al paragrafo 6.3.2.3, [Indemnites] descritta al paragrafo 6.3.2.2. Si trovano tutte nello spazio dei nomi [Pam.Dao.Entites].
La classe [Employe] si evolve come segue:
namespace Pam.Dao.Entites {
public class Employe {
// proprietà automatiche
public virtual int Id { get; set; }
public virtual int Version { get; set; }
public virtual string SS { get; set; }
public virtual string Nom { get; set; }
public virtual string Prenom { get; set; }
public virtual string Adresse { get; set; }
public virtual string Ville { get; set; }
public virtual string CodePostal { get; set; }
public virtual Indemnites Indemnites { get; set; }
// costruttori
public Employe() {
}
// ToString
public override string ToString() {
return string.Format("[{0},{1},{2},{3},{4},{5},{6}]", SS, Nom, Prenom, Adresse, Ville, CodePostal, Indemnites);
}
}
}
7.2.3. La classe [PamException]
Il livello [dao] ha il compito di scambiare dati con una fonte esterna. Questo scambio può fallire. Ad esempio, se le informazioni vengono richieste a un servizio remoto su Internet, il loro recupero fallirà in caso di qualsiasi guasto della rete. In caso di errori di questo tipo, in Java è prassi comune lanciare un'eccezione. Se l’eccezione non è di tipo [RunTimeException] o derivata, è necessario indicare nella firma del metodo che esso genera (throws) un’eccezione. In .NET, tutte le eccezioni sono non controllate, c.a.d. equivalenti al tipo [RunTimeException] di Java. Non è quindi necessario dichiarare che i metodi [GetAllIdentitesEmployes, GetEmploye, GetCotisations] possano generare un'eccezione.
È tuttavia utile poter distinguere le eccezioni l’una dall’altra, poiché la loro gestione può variare. Pertanto, il codice che gestisce diversi tipi di eccezioni può essere scritto nel modo seguente:
try{
... code pouvant générer divers types d'exceptions
}catch (Exception1 ex1){
...on gère un type d'exceptions
}catch (Exception2 ex2){
...on gère un autre type d'exceptions
}finally{
...
}
Creiamo quindi un tipo di eccezioni per il livello [dao] della nostra applicazione. Si tratta del seguente tipo [PamException]:
using System;
namespace Pam.Dao.Entites {
public class PamException : Exception {
// codice di errore
public int Code { get; set; }
// costruttori
public PamException() {
}
public PamException(int Code)
: base() {
this.Code = Code;
}
public PamException(string message, int Code)
: base(message) {
this.Code = Code;
}
public PamException(string message, Exception ex, int Code)
: base(message, ex) {
this.Code = Code;
}
}
}
- riga 2: la classe appartiene allo spazio dei nomi [Pam.Dao.Entites]
- riga 4: la classe deriva dalla classe [Exception]
- riga 7: ha una proprietà pubblica [Code] che è un codice di errore
- nel nostro livello [dao] utilizzeremo due tipi di costruttori:
- quello delle righe 18-21, che possiamo utilizzare come mostrato di seguito:
- (continua)
- oppure quello delle righe 23-26, destinato a segnalare un'eccezione già verificatasi incapsulandola in un'eccezione di tipo [PamException]:
try{
....
}catch (IOException ex){
// si incapsula l'eccezione
throw new PamException("Problème d'accès aux données",ex,10);
}
Questo secondo metodo ha il vantaggio di non perdere le informazioni che può contenere la prima eccezione.
7.2.4. I file delle tabelle di mappatura <--> classi di NHibernate
Torniamo all'architettura dell'applicazione:
![]() |
In fase di lettura, il framework NHibernate utilizza i dati del database e li trasforma in oggetti di cui abbiamo appena presentato le classi. In fase di scrittura, fa il contrario: a partire dagli oggetti, crea, aggiorna ed elimina righe nelle tabelle del database. I file che garantiscono la trasformazione tabelle <--> classi sono già stati presentati:
![]() |
- il file [Cotisations.hbm.xml] presentato al paragrafo 6.3.2.1 stabilisce la corrispondenza tra la tabella [COTISATIONS] e la classe [Cotisations]
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-nhibernate">
<class name="Cotisations" table="COTISATIONS">
<id name="Id" column="ID">
<generator class="native" />
</id>
<version name="Version" column="VERSION"/>
<property name="CsgRds" column="CSGRDS" not-null="true"/>
<property name="Csgd" column="CSGD" not-null="true"/>
<property name="Retraite" column="RETRAITE" not-null="true"/>
<property name="Secu" column="SECU" not-null="true"/>
</class>
</hibernate-mapping>
- il file [Employe.hbm.xml] presentato al paragrafo 6.3.2.3 stabilisce la corrispondenza tra la tabella [EMPLOYES] e la classe [Employe]
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-nhibernate">
<class name="Employe" table="EMPLOYES">
<id name="Id" column="ID">
<generator class="native" />
</id>
<version name="Version" column="VERSION"/>
<property name="SS" column="SS" length="15" not-null="true" unique="true"/>
<property name="Nom" column="NOM" length="30" not-null="true"/>
<property name="Prenom" column="PRENOM" length="20" not-null="true"/>
<property name="Adresse" column="ADRESSE" length="50" not-null="true" />
<property name="Ville" column="VILLE" length="30" not-null="true"/>
<property name="CodePostal" column="CP" length="5" not-null="true"/>
<many-to-one name="Indemnites" column="INDEMNITE_ID" cascade="save-update" lazy="false"/>
</class>
</hibernate-mapping>
- il file [Indemnites.hbm.xml] presentato al paragrafo 6.3.2.2 stabilisce la corrispondenza tra la tabella [INDEMNITES] e la classe [Indemnites]
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-nhibernate">
<class name="Indemnites" table="INDEMNITES">
<id name="Id" column="ID">
<generator class="native" />
</id>
<version name="Version" column="VERSION"/>
<property name="Indice" column="INDICE" not-null="true" unique="true"/>
<property name="BaseHeure" column="BASE_HEURE" not-null="true"/>
<property name="EntretienJour" column="ENTRETIEN_JOUR" not-null="true"/>
<property name="RepasJour" column="REPAS_JOUR" not-null="true" />
<property name="IndemnitesCp" column="INDEMNITES_CP" not-null="true"/>
</class>
</hibernate-mapping>
Si noti che nel tag <hibernate-mapping> di questi file (riga 2) sono presenti i seguenti attributi:
- namespace : Pam.Dao.Entites. Le classi [Cotisations], [Employe] e [Indemnites] devono trovarsi in questo spazio dei nomi.
- assembly: pam-dao-nhibernate. I file di mappatura [*.hbm.xml] devono essere incapsulati in un DLL denominato [pam-dao-nhibernate]. Per ottenere questo risultato, il progetto C# è configurato come segue:
![]() |
- in [1], l’assembly del progetto ha il nome [pam-dao-nhibernate]
- in [2], i file di mappatura [*.hbm.xml] sono integrati nell’assembly del progetto come [3]
7.2.5. L'interfaccia [IPamDao] del livello [dao]
Torniamo all’architettura della nostra applicazione:
![]() |
Nei casi più semplici, si può partire dal livello [metier] per individuare le interfacce dell’applicazione. Per funzionare, l’applicazione necessita di dati:
- già disponibili in file, database o tramite la rete. Questi dati sono forniti dal livello [dao].
- non ancora disponibili. In tal caso, vengono forniti dal livello [ui], che li ottiene dall’utente dell’applicazione.
Quale interfaccia deve offrire il livello [dao] al livello [metier]? Quali sono le possibili interazioni tra questi due livelli? Il livello [dao] deve fornire i seguenti dati al livello [metier]:
- l’elenco delle assistenti materne per consentire all’utente di sceglierne una in particolare
- informazioni complete sulla persona scelta (indirizzo, indice, ...)
- le indennità legate all’indice della persona
- le aliquote dei diversi contributi sociali
Queste informazioni sono infatti note prima del calcolo della busta paga e possono quindi essere memorizzate. Nella direzione [metier] -> [dao], il livello [metier] può richiedere al livello [dao] di registrare il risultato del calcolo della busta paga. In questa sede non lo faremo.
Con queste informazioni, si potrebbe tentare una prima definizione dell’interfaccia del livello [dao]:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// elenco di tutte le identità dei dipendenti
Employe[] GetAllIdentitesEmployes();
// un dipendente specifico con le relative indennità
Employe GetEmploye(string ss);
// elenco di tutti i contributi
Cotisations GetCotisations();
}
}
- riga 1: si importa lo spazio dei nomi delle entità del livello [dao].
- riga 3: il livello [dao] si trova nello spazio dei nomi [Pam.Dao.Service]. Gli elementi dello spazio dei nomi [Pam.Dao.Entites] possono essere creati in più esemplari. Gli elementi dello spazio dei nomi [Pam.Dao.Service] vengono creati in un unico esemplare (singleton). È proprio questo che ha determinato la scelta dei nomi degli spazi dei nomi.
- riga 4: l'interfaccia si chiama [IPamDao]. Definisce tre metodi:
- riga 6, [GetAllIdentitesEmployes] restituisce un array di oggetti di tipo [Employe] che rappresenta l’elenco delle assistenti materne in forma semplificata (cognome, nome, SS).
- riga 8, [GetEmploye] restituisce un oggetto [Employe]: il dipendente con il numero di previdenza sociale passato come parametro al metodo, con le indennità relative al suo indice.
- riga 10, [GetCotisations] restituisce l’oggetto [Cotisations] che incapsula le aliquote dei diversi contributi sociali da prelevare dallo stipendio lordo.
7.3. Implementazione e test del livello [dao]
7.3.1. Il progetto Visual Studio
Il progetto Visual Studio è già stato presentato. Ricordiamolo:
![]() |
- in [1], il progetto nella sua interezza
- in [2], le diverse classi del progetto. La cartella [entites] contiene le entità gestite dal livello [dao] nonché i file di mappatura NHibernate. La cartella [service] contiene l'interfaccia [IPamDao] e la sua implementazione [PamDaoNHibernate]. La cartella [tests] contiene un test da console [Main.cs] e un test unitario [NUnit.cs].
- In [3], i riferimenti del progetto.
7.3.2. Il programma di test da console [Main.cs]
Il programma di test [Main.cs] viene eseguito nella seguente architettura:
![]() |
Ha il compito di testare i metodi dell’interfaccia [IPamDao]. Un esempio di base potrebbe essere il seguente:
using System;
using Pam.Dao.Entites;
using Pam.Dao.Service;
using Spring.Context.Support;
namespace Pam.Dao.Tests {
public class MainPamDaoTests {
public static void Main() {
try {
// istanza del livello [dao]
IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
// elenco delle identità dei dipendenti
foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
Console.WriteLine(Employe.ToString());
}
// un dipendente con le relative indennità
Console.WriteLine("------------------------------------");
Console.WriteLine(pamDao.GetEmploye("254104940426058"));
Console.WriteLine("------------------------------------");
// elenco dei contributi
Cotisations cotisations = pamDao.GetCotisations();
Console.WriteLine(cotisations.ToString());
} catch (Exception ex) {
// visualizzazione eccezione
Console.WriteLine(ex.ToString());
}
//pausa
Console.ReadLine();
}
}
}
- riga 11: si richiede a Spring un riferimento al livello [dao].
- righe 13-15: test del metodo [GetAllIdentitesEmployes] dell’interfaccia [IPamDao]
- riga 18: test del metodo [GetEmploye] dell'interfaccia [IPamDao]
- riga 21: test del metodo [GetCotisations] dell'interfaccia [IPamDao]
Spring, NHibernate e log4net sono configurati dal seguente file [App.config] :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- sezioni di configurazione -->
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
<sectionGroup name="spring">
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
</configSections>
<!-- configurazione Spring -->
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="pamdao" type="Pam.Dao.Service.PamDaoNHibernate, pam-dao-nhibernate" init-method="init" destroy-method="destroy"/>
</objects>
</spring>
<!-- configurazione NHibernate -->
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="dialect">NHibernate.Dialect.MySQLDialect</property>
<property name="connection.connection_string">
Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;
</property>
<property name="show_sql">false</property>
<mapping assembly="pam-dao-nhibernate"/>
</session-factory>
</hibernate-configuration>
<!-- Questa sezione contiene le impostazioni di configurazione di log4net -->
<!-- NOTE IMPORTANTE: i log non sono attivi per impostazione predefinita. Devono essere attivati tramite programma
avec l'instruction log4net.Config.XmlConfigurator.Configure();
! -->
<log4net>
...
</log4net>
</configuration>
La configurazione di NHibernate (riga 10, righe 25-36) è stata illustrata nel paragrafo 6.3.1. Si noti la riga 34, che indica che i file di mappatura si trovano nell'assembly [pam-dao-nhibernate]. Si tratta dell'assembly del progetto.
La configurazione di Spring è definita alle righe 6-9 e 15-22. La riga 20 definisce l'oggetto [pamdao] utilizzato dal programma da console [Main.cs]. Il tag <object> presenta qui i seguenti attributi:
- type: specifica la classe da istanziare. È la classe [PamDaoNHibernate] che implementa l’interfaccia [IPamDao]. Si trova nel file DLL [pam-dao-nhibernate] del progetto.
- init-method: il metodo della classe [PamDaoNHibernate] da eseguire dopo l'istanziazione della classe
- destroy-method: il metodo della classe [PamDaoNHibernate] da eseguire quando il contenitore Spring viene distrutto al termine dell'esecuzione del progetto.
L'esecuzione effettuata con il database descritto al paragrafo 6.2 fornisce il seguente risultato in console:
- righe 1-2: i 2 dipendenti di tipo [Employe] con le sole informazioni [SS, Nom, Prenom]
- riga 4: il dipendente di tipo [Employe] con il numero di previdenza sociale [254104940426058]
- riga 5: le aliquote contributive
7.3.3. Registrazione della classe [PamDaoNHibernate]
![]() |
L'interfaccia [IPamDao] implementata dal livello [dao] è la seguente:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// elenco di tutte le identità dei dipendenti
Employe[] GetAllIdentitesEmployes();
// un dipendente specifico con le relative indennità
Employe GetEmploye(string ss);
// elenco di tutti i contributi
Cotisations GetCotisations();
}
}
Domanda: scrivere il codice della classe [PamDaoNHibernate] che implementa l'interfaccia [IPamDao] sopra riportata utilizzando il framework NHibernate configurato come illustrato in precedenza. Si implementeranno inoltre i metodi init e destroy eseguiti da Spring. Il metodo init creerà la classe SessionFactory, dalla quale si otterranno gli oggetti Session. Il metodo destroy chiuderà questo SessionFactory. Ci si baserà sugli esempi del paragrafo 6.5.
Vincoli:
Si suppone che alcuni dati richiesti al livello [dao] possano essere interamente contenuti in memoria. Pertanto, per migliorare le prestazioni, la classe [PamDaoNHibernate] memorizzerà:
- la tabella [EMPLOYES] sotto forma di (SS, NOM, PRENOM) richiesta dal metodo [GetAllIdentitesEmployes] sotto forma di un array di oggetti di tipo [Employe]
- la tabella [COTISATIONS] sotto forma di un unico oggetto di tipo [Cotisations]
Ciò avverrà nel metodo [init] della classe. Lo scheletro della classe [PamDaoNHibernate] potrebbe essere il seguente:
using System;
...
namespace Pam.Dao.Service {
class PamDaoNHibernate : IPamDao {
// campi privati
private Cotisations cotisations;
private Employe[] employes;
private ISessionFactory sessionFactory = null;
// inizializzazione
public void init() {
try {
// inizializzazione factory
sessionFactory = new Configuration().Configure().BuildSessionFactory();
// si recuperano le aliquote contributive e i dipendenti per memorizzarli nella cache
.......................
}
// chiusura SessionFactory
public void destroy() {
if (sessionFactory != null) {
sessionFactory.Close();
}
}
// elenco di tutte le identità dei dipendenti
public Employe[] GetAllIdentitesEmployes() {
return employes;
}
// un dipendente specifico con le relative indennità
public Employe GetEmploye(string ss) {
................................
}
// elenco dei contributi
public Cotisations GetCotisations() {
return cotisations;
}
}
}
7.3.4. Test unitari con NUnit
Letture consigliate: "Linguaggio C# 2008, Capitolo 4: Architetture a 3 livelli, test NUnit, framework Spring".
Il test precedente era di tipo visivo: si verificava sullo schermo che si ottenessero effettivamente i risultati attesi. Si tratta di un metodo insufficiente in ambito professionale. I test devono sempre essere automatizzati il più possibile e mirare a non richiedere alcun intervento umano. L’essere umano è infatti soggetto alla stanchezza e la sua capacità di verificare i test si affievolisce nel corso della giornata. Lo strumento [NUnit] aiuta a realizzare questa automazione. È disponibile su URL [http://www.nunit.org/].
Il progetto Visual Studio del livello [dao] si evolverà nel modo seguente:
![]() |
- in [1], il programma di test [NUnit.cs]
- in [2,3], il progetto genererà un DLL denominato [pam-dao-nhibernate.dll]
- in [4], il riferimento a DLL del framework NUnit: [nunit.framework.dll]
- in [5], la classe [Main.cs] non sarà inclusa nella DLL [pam-dao-nhibernate]
- in [6], la classe [NUnit.cs] sarà inclusa nella DLL [pam-dao-nhibernate]
La classe di test NUnit è la seguente:
using System.Collections;
using NUnit.Framework;
using Pam.Dao.Service;
using Pam.Dao.Entites;
using Spring.Objects.Factory.Xml;
using Spring.Core.IO;
using Spring.Context.Support;
namespace Pam.Dao.Tests {
[TestFixture]
public class NunitPamDao : AssertionHelper {
// il livello [dao] da testare
private IPamDao pamDao = null;
// costruttore
public NunitPamDao() {
// istanza del livello [dao]
pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
}
// inizializzazione
[SetUp]
public void Init() {
}
[Test]
public void GetAllIdentitesEmployes() {
// verifica del numero di dipendenti
Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
}
[Test]
public void GetCotisations() {
// verifica aliquota contributiva
Cotisations cotisations = pamDao.GetCotisations();
Expect(3.49, EqualTo(cotisations.CsgRds).Within(1E-06));
Expect(6.15, EqualTo(cotisations.Csgd).Within(1E-06));
Expect(9.39, EqualTo(cotisations.Secu).Within(1E-06));
Expect(7.88, EqualTo(cotisations.Retraite).Within(1E-06));
}
[Test]
public void GetEmployeIdemnites() {
// verifica persone fisiche
Employe employe1 = pamDao.GetEmploye("254104940426058");
Employe employe2 = pamDao.GetEmploye("260124402111742");
Expect("Jouveinal", EqualTo(employe1.Nom));
Expect(2.1, EqualTo(employe1.Indemnites.BaseHeure).Within(1E-06));
Expect("Laverti", EqualTo(employe2.Nom));
Expect(1.93, EqualTo(employe2.Indemnites.BaseHeure).Within(1E-06));
}
[Test]
public void GetEmployeIdemnites2() {
// verifica persona inesistente
bool erreur = false;
try {
Employe employe1 = pamDao.GetEmploye("xx");
} catch {
erreur = true;
}
Expect(erreur, True);
}
}
}
- riga 11: la classe presenta l’attributo [TestFixture] che la rende una classe di test [NUnit].
- riga 12: la classe deriva dalla classe di utilità AssertionHelper del framework NUnit (a partire dalla versione 2.4.6).
- riga 14: il campo privato [pamDao] è un'istanza dell'interfaccia di accesso al livello [dao]. Si noti che il tipo di questo campo è un'interfaccia e non una classe. Ciò significa che l'istanza [pamDao] rende accessibili solo i metodi dell'interfaccia [IPamDao].
- I metodi testati nella classe sono quelli con l'attributo [Test]. Per tutti questi metodi, il processo di test è il seguente:
- viene innanzitutto eseguito il metodo con l'attributo [SetUp]. Esso serve a preparare le risorse (connessioni di rete, connessioni ai database, ecc.) necessarie per il test.
- Successivamente viene eseguito il metodo da testare
- e infine viene eseguito il metodo con l'attributo [TearDown]. Esso serve generalmente a liberare le risorse mobilitate dal metodo con l'attributo [SetUp].
- Nel nostro test non ci sono risorse da allocare prima di ogni test e da deallocare in seguito. Pertanto non abbiamo bisogno di metodi con gli attributi [SetUp] e [TearDown]. A titolo di esempio, alle righe 23-26 abbiamo presentato un metodo con l’attributo [SetUp].
- righe 17-20: il costruttore della classe inizializza il campo privato [pamDao] utilizzando Spring e [App.config].
- righe 29-32: testano il metodo [GetAllIdentitesEmployes]
- righe 35-42: testano il metodo [GetCotisations]
- righe 45-53: testano il metodo [GetEmploye]
- righe 56-65: testano il metodo [GetEmploye] in caso di eccezione.
La generazione del progetto crea i file DLL e [pam-dao-nhibernate.dll] nella cartella [bin/Release].
![]() |
La cartella [bin/Release] contiene inoltre:
- i file DLL che fanno parte dei riferimenti del progetto e che hanno l'attributo [Copie locale] impostato su vero: [Spring.Core, MySql.data, NHibernate, log4net]. Questi DLL sono accompagnati dalle copie dei DLL che essi stessi utilizzano:
- [CastleDynamicProxy, Iesi.Collections] per lo strumento NHibernate
- [antlr.runtime, Common.Logging] per lo strumento Spring
- il file [pam-dao-nhibernate.dll.config] è una copia del file di configurazione [App.config]. È VS che esegue questa duplicazione. All'esecuzione viene utilizzato il file [pam-dao-nhibernate.dll.config] e non [App.config].
Si caricano i file DLL e [pam-dao-nhibernate.dll] con lo strumento [NUnit-Gui], versione 2.4.6, e si eseguono i test:

Come si vede sopra, i test hanno avuto esito positivo.
Esercitazione pratica:
eseguire sul computer i test della classe [PamDaoNHibernate].- utilizzare diversi file di configurazione [App.config] per utilizzare diversi SGBD (Firebird, MySQL, Postgres, SQL Server)
7.3.5. Generazione del e il DLL dal livello [dao]
Una volta scritta e testata la classe [PamDaoNHibernate], si genererà la classe DLL dal livello [dao] nel modo seguente:
![]() |
- [1], i programmi di test sono esclusi dall'assemblaggio del progetto
- [2,3], configurazione del progetto
- [4], generazione del progetto
- il file DLL viene generato nella cartella [bin/Release] [5]. Lo aggiungiamo ai file DLL già presenti nella cartella [lib] [6]:
![]() |
7.4. Il livello applicativo
Torniamo all’architettura generale dell’applicazione [SimuPaie]:
![]() |
Consideriamo ora che il livello [dao] sia stato implementato e incapsulato in DLL [pam-dao-nhibernate.dll]. Ci concentriamo ora sul livello [metier]. È questo che implementa le regole di business, in questo caso le regole per il calcolo dello stipendio.
7.4.1. Il progetto Visual Studio " " del livello [metier]
Il progetto Visual Studio del livello di business potrebbe presentarsi come segue:
![]() |
- in [1] l’intero progetto configurato dal file [App.config]
- in [2], il livello [metier] è costituito dalle due cartelle [entites, service]. La cartella [tests] contiene un programma di test da console (Main.cs) e un programma di test NUnit (NUnit.cs).
- In [3] sono riportati i riferimenti utilizzati dal progetto. Si notino i riferimenti DLL e [pam-dao-nhibernate] del livello [dao] esaminato in precedenza.
7.4.2. L'interfaccia [IPamMetier] del livello [metier]
Torniamo all’architettura generale dell’applicazione:
![]() |
Quale interfaccia deve fornire il livello [metier] al livello [ui]? Quali sono le possibili interazioni tra questi due livelli? Ricordiamo l’interfaccia web che verrà presentata all’utente:
![]() |
- alla visualizzazione iniziale del modulo, in [1] deve essere presente l'elenco dei dipendenti. È sufficiente un elenco semplificato (Cognome, Nome, SS). Il numero SS è necessario per accedere alle informazioni aggiuntive sul dipendente selezionato (informazioni da 6 a 11).
- Le informazioni da 12 a 15 indicano le diverse aliquote contributive.
- Le informazioni da 16 a 19 sono le indennità legate all’indice del dipendente
- Le informazioni da 20 a 24 sono le componenti salariali calcolate in base ai dati inseriti dall’utente nelle voci da 1 a 3.
L'interfaccia [IPamMetier] fornita al livello [ui] dal livello [metier] deve soddisfare i requisiti sopra indicati. Esistono numerose interfacce possibili. Proponiamo la seguente:
using Pam.Dao.Entites;
using Pam.Metier.Entites;
namespace Pam.Metier.Service {
public interface IPamMetier {
// elenco di tutte le identità dei dipendenti
Employe[] GetAllIdentitesEmployes();
// ------- calcolo dello stipendio
FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
}
}
- riga 7: il metodo che consentirà il popolamento del menu a tendina [1]
- riga 10: il metodo che consentirà di ottenere le informazioni da 6 a 24. Queste sono state raggruppate in un oggetto di tipo [FeuilleSalaire].
7.4.3. Le entità del livello [metier]
La cartella [entites] del progetto Visual Studio contiene gli oggetti gestiti dalla classe di business: [FeuilleSalaire] e [ElementsSalaire].
![]() |
La classe [FeuilleSalaire] racchiude le informazioni da 6 a 24 del modulo precedente:
using Pam.Dao.Entites;
namespace Pam.Metier.Entites {
public class FeuilleSalaire {
// proprietà automatiche
public Employe Employe { get; set; }
public Cotisations Cotisations { get; set; }
public ElementsSalaire ElementsSalaire { get; set; }
// ToString
public override string ToString() {
return string.Format("[{0},{1},{2}", Employe, Cotisations, ElementsSalaire);
}
}
}
- riga 8: le informazioni da 6 a 11 relative al dipendente di cui si calcola lo stipendio e le informazioni da 16 a 19 relative alle sue indennità. È importante ricordare che un oggetto [Employe] racchiude un oggetto [Indemnites] che rappresenta le indennità del dipendente.
- riga 9: le informazioni da 12 a 15
- riga 10: le informazioni da 20 a 24
- righe 13-15: il metodo [ToString]
La classe [ElementsSalaire] incapsula le informazioni da 20 a 24 del modulo:
namespace Pam.Metier.Entites {
public class ElementsSalaire {
// proprietà automatiche
public double SalaireBase { get; set; }
public double CotisationsSociales { get; set; }
public double IndemnitesEntretien { get; set; }
public double IndemnitesRepas { get; set; }
public double SalaireNet { get; set; }
// ToString
public override string ToString() {
return string.Format("[{0} : {1} : {2} : {3} : {4} ]", SalaireBase, CotisationsSociales, IndemnitesEntretien, IndemnitesRepas, SalaireNet);
}
}
}
- righe 4-8: le componenti dello stipendio come spiegato nelle regole di business descritte al paragrafo 3.2.
- riga 4: la retribuzione base del dipendente, in funzione del numero di ore lavorate
- riga 5: i contributi trattenuti su tale stipendio base
- righe 6 e 7: le indennità da aggiungere alla retribuzione base, in funzione dell’indice del dipendente e del numero di giorni lavorati
- riga 8: la retribuzione netta da corrispondere
- righe 12-15: il metodo [ToString] della classe.
7.4.4. Implementazione del livello [metier]
![]() |
Implementeremo l’interfaccia [IPamMetier] con due classi:
- [AbstractBasePamMetier], che è una classe astratta in cui implementeremo l’accesso ai dati dell’interfaccia [IPamMetier]. Questa classe avrà un riferimento al livello [dao].
- [PamMetier], una classe derivata da [AbstractBasePamMetier], che a sua volta implementerà le regole di business dell’interfaccia [IPamMetier]. Essa non avrà alcuna conoscenza del livello [dao].
La classe [AbstractBasePamMetier] sarà la seguente:
using Pam.Dao.Entites;
using Pam.Dao.Service;
using Pam.Metier.Entites;
namespace Pam.Metier.Service {
public abstract class AbstractBasePamMetier : IPamMetier {
// oggetto di accesso ai dati
public IPamDao PamDao { get; set; }
// elenco di tutte le identità dei dipendenti
public Employe[] GetAllIdentitesEmployes() {
return PamDao.GetAllIdentitesEmployes();
}
// un dipendente specifico con le relative indennità
protected Employe GetEmploye(string ss) {
return PamDao.GetEmploye(ss);
}
// i contributi
protected Cotisations GetCotisations() {
return PamDao.GetCotisations();
}
// il calcolo dello stipendio
public abstract FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
}
}
- riga 5: la classe appartiene allo spazio dei nomi [Pam.Metier.Service] come tutte le classi e le interfacce del livello [metier].
- riga 6: la classe è astratta (attributo abstract) e implementa l’interfaccia [IPamMetier]
- riga 9: la classe contiene un riferimento al livello [dao] sotto forma di una proprietà pubblica
- righe 12-14: implementazione del metodo [GetAllIdentitesEmployes] dell’interfaccia [IPamMetier] – utilizza il metodo omonimo del livello [dao]
- righe 17-19: metodo interno (protected) [GetEmploye] che richiama il metodo omonimo del livello [dao] – dichiarato protected affinché le classi derivate possano accedervi senza che sia pubblico.
- righe 22-24: metodo interno (protected) [GetCotisations] che richiama il metodo omonimo del livello [dao]
- riga 27: implementazione astratta (attributo abstract) del metodo [GetSalaire] dell'interfaccia [IPamMetier].
Il calcolo dello stipendio è implementato dalla seguente classe [PamMetier]:
using System;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
namespace Pam.Metier.Service {
public class PamMetier : AbstractBasePamMetier {
// calcolo dello stipendio
public override FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés) {
// SS: numero identificativo del dipendente
// HeuresTravaillées: numero di ore lavorate
// Giorni lavorati: numero di giorni lavorati
// si recupera il dipendente con le relative indennità
...
// si recuperano le varie aliquote contributive
...
// si calcolano le voci della retribuzione
...
// si genera la busta paga
return ...;
}
}
}
- riga 7: la classe deriva da [AbstractBasePamMetier] e quindi implementa l'interfaccia [IPamMetier]
- riga 10: il metodo [GetSalaire] da implementare
Domanda: scrivere il codice del metodo [GetSalaire].
7.4.5. Il test da console del livello [metier]
Ricordiamo il progetto Visual Studio del livello [metier]:
![]() |
Il programma di test [Main] sopra riportato verifica i metodi dell’interfaccia [IPamMetier]. Un esempio di base potrebbe essere il seguente:
using System;
using Pam.Dao.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;
namespace Pam.Metier.Tests {
class MainPamMetierTests {
public static void Main() {
try {
// istanza del livello [metier]
IPamMetier pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
// calcoli dei fogli paga
Console.WriteLine(pamMetier.GetSalaire("260124402111742", 30, 5));
Console.WriteLine(pamMetier.GetSalaire("254104940426058", 150, 20));
try {
Console.WriteLine(pamMetier.GetSalaire("xx", 150, 20));
} catch (PamException ex) {
Console.WriteLine(string.Format("PamException : {0}", ex.Message));
}
} catch (Exception ex) {
Console.WriteLine(string.Format("Exception : {0}", ex.ToString()));
}
// pausa
Console.ReadLine();
}
}
}
- riga 11: istanziazione da parte di Spring del livello [metier].
- righe 13-14: test del metodo [GetSalaire] dell’interfaccia [IPamMetier]
- righe 15-22: test del metodo [GetSalaire] quando si verifica un'eccezione
Il programma di test utilizza il file di configurazione [App.config] come segue:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- sezioni di configurazione -->
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
<sectionGroup name="spring">
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
</configSections>
<!-- configurazione Spring -->
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="pamdao" type="Pam.Dao.Service.PamDaoNHibernate, pam-dao-nhibernate" init-method="init" destroy-method="destroy"/>
<object id="pammetier" type="Pam.Metier.Service.PamMetier, pam-metier-dao-nhibernate" >
<property name="PamDao" ref="pamdao"/>
</object>
</objects>
</spring>
<!-- configurazione NHibernate -->
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
....
</hibernate-configuration>
<!-- Questa sezione contiene le impostazioni di configurazione di log4net -->
<!-- NOTE IMPORTANTE: i log non sono attivi per impostazione predefinita. È necessario attivarli tramite programma
avec l'instruction log4net.Config.XmlConfigurator.Configure();
! -->
<log4net>
...
</log4net>
</configuration>
Questo file è identico al file [App.config] utilizzato per il progetto del livello [dao] (cfr. paragrafo 7.3.2), salvo le seguenti differenze:
- riga 20: l'oggetto con ID "pamdao" è di tipo [Pam.Dao.Service.PamDaoNHibernate] e si trova nell'assembly [pam-dao-nhibernate]. Il livello [dao] è quello esaminato in precedenza.
- righe 21-23: l'oggetto con ID "pammetier" è di tipo [Pam.Metier.Service.PamMetier] e si trova nell'assembly [pam-metier-dao-nhibernate]. È necessario configurare il progetto in questo modo:
![]() |
- riga 22: l’oggetto [PamMetier] istanziato da Spring ha una proprietà pubblica [PamDao] che è un riferimento al livello [dao]. Questa proprietà viene inizializzata con il riferimento al livello [dao] creato alla riga 20.
L'esecuzione effettuata con il database descritto al paragrafo 6.2 restituisce il seguente risultato in console:
- righe 1-2: i 2 fogli paga richiesti
- riga 3: eccezione di tipo [PamException] causata da un dipendente inesistente.
7.4.6. Test unitari del livello business
Il test precedente era visivo: si verificava sullo schermo che si ottenessero effettivamente i risultati attesi. Passiamo ora ai test non visivi NUnit.
Torniamo al progetto Visual Studio del progetto [metier]:
![]() |
- in [1], il programma di test NUnit
- in [2], il riferimento su DLL [nunit.framework]
![]() |
- in [3,4], la generazione del progetto produrrà DLL e [pam-metier-dao-nhibernate.dll].
- in [5], il file [NUnit.cs] sarà incluso nell'assembly [pam-metier-dao-nhibernate.dll] ma non in [Main.cs] e [6]
La classe di test NUnit è la seguente:
using NUnit.Framework;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;
namespace Pam.Metier.Tests {
[TestFixture()]
public class NunitTestPamMetier : AssertionHelper {
// il livello [metier] da testare
private IPamMetier pamMetier;
// costruttore
public NunitTestPamMetier() {
// istanziazione del livello [dao]
pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
}
[Test]
public void GetAllIdentitesEmployes() {
// verifica del numero di dipendenti
Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
}
[Test]
public void GetSalaire1() {
// calcolo di una busta paga
FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
// verifiche
Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
// busta paga di un dipendente inesistente
bool erreur = false;
try {
feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
} catch (PamException) {
erreur = true;
}
Expect(erreur, True);
}
}
}
- riga 13: il campo privato [pamMetier] è un'istanza dell'interfaccia di accesso al livello [metier]. Si noti che il tipo di questo campo è un'interfaccia e non una classe. Ciò significa che l'istanza [PamMetier] rende accessibili solo i metodi dell'interfaccia [IPamMetier].
- righe 16-19: il costruttore della classe inizializza il campo privato [pamMetier] utilizzando Spring e il file di configurazione [App.config].
- righe 23-26: testano il metodo [GetAllIdentitesEmployes]
- righe 29-42: testano il metodo [GetSalaire]
Il progetto sopra riportato genera i file DLL e [pam-metier.dll] nella cartella [bin/Release].
![]() |
La cartella [bin/Release] contiene inoltre:
- i file DLL che fanno parte dei riferimenti del progetto e che hanno l'attributo [Copie locale] impostato su vero: [Spring.Core, MySql.data, NHibernate, log4net, pam-dao-nhibernate]. Questi DLL sono accompagnati dalle copie dei DLL che essi stessi utilizzano:
- [CastleDynamicProxy, Iesi.Collections] per lo strumento NHibernate
- [antlr.runtime, Common.Logging] per lo strumento Spring
- il file [pam-metier-dao-nhibernate.dll.config] è una copia del file di configurazione [App.config].
Si caricano i file DLL e [pam-metier-dao-nhibernate.dll] con lo strumento [NUnit-Gui, version 2.4.6] e si eseguono i test:

Come si vede sopra, i test hanno avuto esito positivo.
Esercitazione pratica:
implementare sul computer i test della classe [PamMetier].- utilizzare diversi file di configurazione App.config per utilizzare diversi SGBD (Firebird, MySQL, Postgres, SQL Server)
7.4.7. Generazione del file DLL dal livello [metier]
Una volta scritta e testata la classe [PamMetier], si genererà la DLL [pam-metier-dao-nhibernate.dll] dal livello [metier] seguendo il metodo descritto al paragrafo 7.3.5 Si avrà cura di non includere nella DLL i programmi di test [Main.cs] e [NUnit.cs]. Successivamente, lo si inserirà nella cartella [lib] contenente i file DLL e [1].
![]() |
7.5. Il livello [web]
Torniamo all’architettura generale dell’applicazione [SimuPaie]:
![]() |
Consideriamo che i livelli [dao] e [métier] siano già realizzati e incapsulati nei livelli DLL e [pam-dao-nhibernate, pam-metier-dao-nhibernate.dll]. Descriviamo ora il livello web.
7.5.1. Il progetto Visual Web Developer del livello [web]
![]() |
- in [1], il progetto nel suo complesso:
- [Global.asax]: la classe istanziata all’avvio dell’applicazione web che garantisce l’inizializzazione dell’applicazione
- [Default.aspx]: la pagina del modulo web
- in [2], i DLL necessari all'applicazione web. Da notare i DLL dei livelli [dao] e [metier] creati in precedenza.
7.5.2. Configurazione dell’applicazione
Il file [Web.config], che configura l’applicazione, definisce gli stessi dati del file [App.config] che configura il livello [metier] esaminato in precedenza. Questi devono essere inseriti nel codice pregenerato del file [Web.config]:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<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>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<!-- configurazione Spring -->
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="pamdao" type="Pam.Dao.Service.PamDaoNHibernate, pam-dao-nhibernate" init-method="init" destroy-method="destroy"/>
<object id="pammetier" type="Pam.Metier.Service.PamMetier, pam-metier-dao-nhibernate" >
<property name="PamDao" ref="pamdao"/>
</object>
</objects>
</spring>
<!-- configurazione NHibernate -->
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<!--
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
-->
<property name="dialect">NHibernate.Dialect.MySQLDialect</property>
<property name="connection.connection_string">
Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;
</property>
<property name="show_sql">false</property>
<mapping assembly="pam-dao-nhibernate"/>
</session-factory>
</hibernate-configuration>
<!-- Questa sezione contiene le impostazioni di configurazione di log4net -->
<!-- NOTE IMPORTANTE: i log non sono attivi per impostazione predefinita. È necessario attivarli tramite programma
avec l'instruction log4net.Config.XmlConfigurator.Configure();
! -->
<log4net>
....
</log4net>
<appSettings/>
<connectionStrings/>
<system.web>
....
....
</configuration>
Nelle righe 9-12, 18-28 e 31-44 si ritrova la configurazione Spring e NHibernate descritta nel file [App.config] del livello [metier] (cfr. paragrafo 7.4.5).
Global.asax.cs
using System;
using Pam.Dao.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;
namespace pam_v3
{
public class Global : System.Web.HttpApplication
{
// --- dati statici dell'applicazione ---
public static Employe[] Employes;
public static IPamMetier 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 IPamMetier;
// 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;
}
}
}
}
Si ricorda che:
- la classe [Global.asax.cs] viene istanziata all'avvio dell'applicazione e che tale istanza è accessibile a tutte le richieste di tutti gli utenti. I campi statici delle righe 11-14 sono quindi condivisi tra tutti gli utenti.
- il metodo [Application_Start] viene eseguito una sola volta dopo l'istanziazione della classe. È in questo metodo che generalmente viene eseguita l'inizializzazione dell'applicazione.
I dati condivisi da tutti gli utenti sono i seguenti:
- riga 11: l’array di oggetti di tipo [Employe] che memorizzerà l’elenco semplificato (SS, NOM, PRENOM) di tutti i dipendenti
- riga 12: un riferimento al livello [metier] incapsulato in DLL [pam-metier-dao-nhibernate.dll]
- riga 13: un messaggio che indica l'esito dell'inizializzazione (corretta o con errore)
- riga 14: un valore booleano che indica se l'inizializzazione si è conclusa con un errore o meno.
In [Application_Start]:
- riga 23: Spring istanzia i livelli [metier] e [dao] e restituisce un riferimento al livello [metier]. Quest’ultimo viene memorizzato nel campo statico [PamMetier] della riga 12.
- riga 25: viene richiesto l’array dei dipendenti al livello [metier]
- riga 27: il messaggio in caso di esito positivo
- riga 32: il messaggio in caso di errore
7.5.3. Il modulo [Default.aspx]
Il modulo è quello della versione 2.

Domanda: Ispirandovi al codice C# della pagina [Default.aspx.cs] della versione 2, scrivete il codice [Default.aspx.cs] della versione 3. L'unica differenza sta nel calcolo dello stipendio. Mentre nella versione 2 si utilizzava il metodo API ADO.NET per recuperare le informazioni dal database, qui si utilizzerà il metodo GetSalaire del livello [metier].
Esercitazione pratica:
implementare sul computer l’applicazione web precedente- utilizzare diversi file di configurazione [Web.config] per impiegare diversi SGBD (Firebird, MySQL, Postgres, SQL Server)





























