7. L'applicazione [SimuPaie] – versione 3 – Architettura a 3 livelli con NHibernate
Lettura consigliata: "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) gestirà l'accesso ai dati.
- Il livello [2-business] gestirà la logica di business dell'applicazione, in particolare i calcoli delle buste paga.
- Il livello [3-ui] (ui = User Interface) gestirà la presentazione dei dati all'utente e l'esecuzione delle richieste dell'utente. Ci riferiamo all'insieme di moduli che svolgono questa funzione come [Applicazione]. Funge da interfaccia utente.
- I tre livelli saranno resi indipendenti tramite l’uso delle interfacce .NET
- L'integrazione dei diversi livelli sarà gestita da Spring IoC
L'elaborazione di una richiesta del cliente segue questi passaggi:
- Il client invia una richiesta all'applicazione.
- L'applicazione elabora questa richiesta. A tal fine, potrebbe aver bisogno dell'assistenza del livello [business], il quale a sua volta potrebbe ricorrere al livello [DAO] qualora fosse necessario scambiare dati con il database.
- L'applicazione riceve una risposta dal livello [business]. Sulla base di questa risposta, invia la vista appropriata (= la risposta) al client.
Prendiamo l'esempio del calcolo della retribuzione di un assistente all'infanzia. Ciò richiederà diversi passaggi:
![]() |
- Il livello [UI] dovrà chiedere all'utente
- l'identità della persona di cui si deve calcolare la busta paga
- il numero di giorni lavorati da quella persona
- il numero di ore lavorate
- Per farlo, dovrà presentare all'utente un elenco di persone (cognome, nome, codice fiscale) dalla tabella [EMPLOYEES] in modo che l'utente possa selezionarne una. Il livello [ui] utilizzerà il percorso [2, 3, 4, 5, 6, 7] per recuperare queste informazioni. L'operazione [2] è la richiesta dell'elenco dei dipendenti; l'operazione [7] è la risposta a tale richiesta. Una volta fatto ciò, il livello [ui] può presentare l'elenco dei dipendenti all'utente tramite [8].
- L'utente invierà al livello [ui] il numero di giorni lavorati e il numero di ore lavorate. Questa è l'operazione [1] sopra indicata. Durante questa fase, l'utente interagisce solo con il livello [ui]. È questo livello che verificherà la validità dei dati inseriti. Una volta fatto ciò, l'utente richiederà il calcolo della busta paga.
- Il livello [ui] chiederà al livello business di eseguire questo calcolo. A tal fine, invierà i dati ricevuti dall'utente al livello business. Questa è l'operazione [2].
- Il livello [business] necessita di alcune informazioni per svolgere il proprio compito:
- informazioni più complete sulla persona (indirizzo, indice, ecc.)
- indennità legate alla propria categoria retributiva
- le aliquote dei vari contributi previdenziali da detrarre dallo stipendio lordo
Richiederà queste informazioni al livello [DAO] utilizzando il percorso [3, 4, 5, 6]. [3] è la richiesta iniziale e [6] è la risposta a tale richiesta.
- Una volta ottenuti tutti i dati necessari, il livello [business] calcola la retribuzione della persona selezionata dall’utente.
- Il livello [business] può ora rispondere alla richiesta del livello [ui] effettuata in (d). Questo è il percorso [7].
- Il livello [ui] formatterà questi risultati per presentarli all'utente in una forma appropriata e poi li visualizzerà. Questo è il percorso [8].
- Si può immaginare che questi risultati debbano essere memorizzati in un file o in un database. Ciò può essere fatto automaticamente. In questo caso, dopo l'operazione (f), il livello [business] chiederà al livello [DAO] di salvare i risultati. Questo sarà il percorso [3, 4, 5, 6]. Ciò può essere fatto anche su richiesta dell'utente. Il percorso [1-8] sarà utilizzato dal ciclo richiesta-risposta.
Come si vede in questa descrizione, un livello utilizza le risorse del livello alla sua destra, mai quelle del livello alla sua sinistra.
La nostra prima implementazione di questa architettura a tre livelli sarà un'applicazione ASP.NET in cui
- i livelli [DAO] e [business] saranno implementati da DLL
- e il livello [ui] sarà implementato dal web form della versione 1 (vedi sezione 4.2.1).
Iniziamo implementando il livello [DAO] utilizzando il framework NHibernate.
7.2. Il livello di accesso ai dati [DAO]
![]() |
7.2.1. Il progetto Visual Studio C# per il livello [DAO]
Il progetto Visual Studio per il livello [dao] è il seguente:
![]() |
- in [1], il progetto nel suo complesso
- in [2], le varie classi del progetto
- in [3], i riferimenti del progetto.
- in [4], una cartella [lib] contenente le DLL necessarie per i vari progetti che seguono
Nei riferimenti del progetto [3] si trovano le seguenti DLL:
- NHibernate: per l'ORM NHibernate
- MySql.Data: il driver ADO.NET per il DBMS MySQL
- Spring.Core: per il framework Spring
- log4net: una libreria di logging
- nunit.framework: una libreria per i test unitari
Questi riferimenti sono stati presi dalla cartella [lib] [4]. Assicurati che la proprietà "Copia locale" per tutti questi riferimenti sia impostata su "True" [5]:
![]() |
7.2.2. Entità nel livello [dao]
![]() |
Le entità (oggetti) necessarie per il livello [dao] sono state raccolte nella cartella [entities] del progetto. Alcune ci sono già familiari: [Contributions] descritta nella sezione 6.3.2.1, [Employee] descritta nella sezione 6.3.2.3, [Allowances] descritta nella sezione 6.3.2.2. Si trovano tutte nello spazio dei nomi [Pam.Dao.Entities].
La classe [Employee] è definita come segue:
namespace Pam.Dao.Entites {
public class Employe {
// automatic properties
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; }
// manufacturers
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] è responsabile dello scambio di dati con una fonte esterna. Questo scambio potrebbe fallire. Ad esempio, se vengono richieste informazioni da un servizio remoto su Internet, il recupero fallirà a causa di un'eventuale interruzione della rete. Per questo tipo di errore, è prassi standard in Java generare un'eccezione. Se l'eccezione non è di tipo [RunTimeException] o di un tipo derivato, è necessario indicare nella firma del metodo che il metodo genera un'eccezione. In .NET, tutte le eccezioni sono non gestite, ovvero equivalenti al tipo [RunTimeException] di Java. Pertanto, non è necessario dichiarare che i metodi [GetAllEmployeeIDs, GetEmployee, GetContributions] potrebbero generare un'eccezione.
Tuttavia, è utile poter distinguere tra diverse eccezioni, poiché la loro gestione può variare. Pertanto, il codice che gestisce vari tipi di eccezioni può essere scritto come segue:
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 eccezione per il livello [DAO] della nostra applicazione. Si tratta del seguente tipo [PamException]:
using System;
namespace Pam.Dao.Entites {
public class PamException : Exception {
// the error code
public int Code { get; set; }
// manufacturers
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.Entities]
- riga 4: la classe deriva dalla classe [Exception]
- riga 7: ha una proprietà pubblica [Code] che è un codice di errore
- Nel nostro livello [dao], useremo due tipi di costruttori:
- quello alle righe 18–21, che può essere utilizzato come mostrato di seguito:
- (continua)
- oppure quello alle righe 23–26, progettato per propagare un'eccezione già verificatasi avvolgendo la stessa in un'eccezione [PamException]:
try{
....
}catch (IOException ex){
// on encapsule l'exception
throw new PamException("Problème d'accès aux données",ex,10);
}
Questo secondo metodo ha il vantaggio di non perdere le informazioni contenute nella prima eccezione.
7.2.4. File di mappatura NHibernate <--> classi
Torniamo all'architettura dell'applicazione:
![]() |
In fase di lettura, il framework NHibernate recupera i dati dal database e li trasforma in oggetti, le cui classi abbiamo appena presentato. In fase di scrittura, fa il contrario: partendo dagli oggetti, crea, aggiorna ed elimina righe nelle tabelle del database. I file responsabili della trasformazione tabella <--> classe sono già stati presentati:
![]() |
- il file [Cotisations.hbm.xml] presentato nella sezione 6.3.2.1 mappa la tabella [COTISATIONS] alla 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 nella sezione 6.3.2.3 mappa la tabella [EMPLOYEES] alla classe [Employee]
<?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 nella sezione 6.3.2.2 mappa la tabella [INDEMNITES] alla 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.Entities. Le classi [Contributions], [Employee] e [Allowances] devono trovarsi in questo namespace.
- assembly: pam-dao-nhibernate. I file di mappatura [*.hbm.xml] devono essere incapsulati in una DLL denominata [pam-dao-nhibernate]. A tal fine, il progetto C# è configurato come segue:
![]() |
- in [1], l'assembly del progetto è denominato [pam-dao-nhibernate]
- in [2], i file di mappatura [*.hbm.xml] sono inclusi [3] nell'assembly del progetto
7.2.5. L'interfaccia [ IPamDao] del livello [DAO]
Torniamo all'architettura della nostra applicazione:
![]() |
In casi semplici, possiamo partire dal livello [business] per individuare le interfacce dell'applicazione. Per funzionare, ha bisogno di dati:
- già disponibili in file, database o tramite la rete. Questi sono forniti dal livello [dao].
- non ancora disponibili. Vengono quindi forniti dal livello [ui], che li ottiene dall'utente dell'applicazione.
Quale interfaccia dovrebbe fornire il livello [DAO] al livello [business]? Quali interazioni sono possibili tra questi due livelli? Il livello [DAO] deve fornire i seguenti dati al livello [business]:
- l'elenco dei fornitori di servizi di assistenza all'infanzia per consentire all'utente di selezionarne uno specifico
- informazioni complete sulla persona selezionata (indirizzo, indice, ecc.)
- le indennità associate al codice della persona
- le aliquote dei vari contributi previdenziali
Queste informazioni sono note prima del calcolo delle buste paga e possono quindi essere memorizzate. Nella direzione [business] -> [DAO], il livello [business] può richiedere al livello [DAO] di registrare il risultato del calcolo delle buste paga. Non lo faremo in questa sede.
Con queste informazioni, potremmo tentare una definizione iniziale dell'interfaccia del livello [dao]:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// list of all employee identities
Employe[] GetAllIdentitesEmployes();
// an individual employee with benefits
Employe GetEmploye(string ss);
// list of all contributions
Cotisations GetCotisations();
}
}
- Riga 1: Importiamo lo spazio dei nomi per le entità nel livello [dao].
- Riga 3: Il livello [dao] si trova nello spazio dei nomi [Pam.Dao.Service]. Gli elementi nello spazio dei nomi [Pam.Dao.Entities] possono essere creati in più istanze. Gli elementi nello spazio dei nomi [Pam.Dao.Service] vengono creati come singola istanza (singleton). Questo è ciò che ha giustificato 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 dei fornitori di servizi di assistenza all'infanzia in un formato semplificato (cognome, nome, SS).
- Riga 8: [GetEmploye] restituisce un oggetto [Employe]: il dipendente con il numero di previdenza sociale passato come parametro al metodo, insieme ai benefici associati al suo livello retributivo.
- Riga 10, [GetCotisations] restituisce l'oggetto [Cotisations], che incapsula le aliquote dei vari contributi previdenziali da detrarre 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. Ricordiamo che:
![]() |
- in [1], il progetto nel suo complesso
- in [2], le varie classi del progetto. La cartella [entities] contiene le entità gestite dal livello [dao] e 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:
![]() |
È responsabile del test dei 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 {
// layer instantiation [dao]
IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
// list of employee identities
foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
Console.WriteLine(Employe.ToString());
}
// an employee with benefits
Console.WriteLine("------------------------------------");
Console.WriteLine(pamDao.GetEmploye("254104940426058"));
Console.WriteLine("------------------------------------");
// list of contributions
Cotisations cotisations = pamDao.GetCotisations();
Console.WriteLine(cotisations.ToString());
} catch (Exception ex) {
// exception display
Console.WriteLine(ex.ToString());
}
//break
Console.ReadLine();
}
}
}
- Riga 11: chiediamo a Spring un riferimento al livello [dao].
- righe 13–15: test del metodo [GetAllEmployeeIDs] 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>
<!-- configuration sections -->
<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>
<!-- spring configuration -->
<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>
<!-- configuration 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>
<!-- This section contains the log4net configuration settings -->
<!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
avec l'instruction log4net.Config.XmlConfigurator.Configure();
! -->
<log4net>
...
</log4net>
</configuration>
La configurazione di NHibernate (riga 10, righe 25–36) è stata spiegata nella Sezione 6.3.1. Si noti la riga 34, che specifica che i file di mappatura si trovano nell'assembly [pam-dao-nhibernate]. Questo è l'assembly del progetto.
La configurazione Spring è definita alle righe 6–9 e 15–22. La riga 20 definisce l'oggetto [pamdao] utilizzato dal programma console [Main.cs]. Il tag <object> presenta qui i seguenti attributi:
- type: specifica la classe da istanziare. Si tratta della classe [PamDaoNHibernate], che implementa l'interfaccia [IPamDao]. Si trova nella 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 utilizzando il database descritto nella Sezione 6.2 produce il seguente output della console:
- righe 1-2: i 2 dipendenti di tipo [Employee] con solo le informazioni [SS, Cognome, Nome]
- riga 4: il dipendente di tipo [Employee] con numero di previdenza sociale [254104940426058]
- riga 5: aliquote contributive
7.3.3. Definizione della classe [PamDaoNHibernate]
![]() |
L'interfaccia [IPamDao] implementata dal livello [dao] è la seguente:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// list of all employee identities
Employe[] GetAllIdentitesEmployes();
// an individual employee with benefits
Employe GetEmploye(string ss);
// list of all contributions
Cotisations GetCotisations();
}
}
Domanda: Scrivere il codice per la classe [PamDaoNHibernate] che implementa l'interfaccia [IPamDao] sopra riportata utilizzando il framework NHibernate configurato come descritto in precedenza. Implementeremo anche i metodi init e destroy eseguiti da Spring. Il metodo init creerà la SessionFactory da cui otterremo gli oggetti Session. Il metodo destroy chiuderà questa SessionFactory. Utilizzeremo gli esempi della Sezione 6.5.
Vincoli:
Supporremo che alcuni dati richiesti dal livello [dao] possano essere interamente contenuti in memoria. Pertanto, per migliorare le prestazioni, la classe [PamDaoNHibernate] memorizzerà:
- la tabella [EMPLOYEES] nel formato (SS, LAST_NAME, FIRST_NAME) richiesto dal metodo [GetAllEmployeeIDs], come un array di oggetti [Employee]
- la tabella [COTISATIONS] sotto forma di un singolo 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 {
// private fields
private Cotisations cotisations;
private Employe[] employes;
private ISessionFactory sessionFactory = null;
// init
public void init() {
try {
// factory initialization
sessionFactory = new Configuration().Configure().BuildSessionFactory();
// retrieve contribution rates and employees for caching
.......................
}
// closure SessionFactory
public void destroy() {
if (sessionFactory != null) {
sessionFactory.Close();
}
}
// list of all employee identities
public Employe[] GetAllIdentitesEmployes() {
return employes;
}
// an individual employee with benefits
public Employe GetEmploye(string ss) {
................................
}
// list of contributions
public Cotisations GetCotisations() {
return cotisations;
}
}
}
7.3.4. Test unitari con NUnit
Lettura consigliata: "C# 2008, Capitolo 4: Architetture a tre livelli, test NUnit, framework Spring".
Il test precedente era visivo: abbiamo controllato sullo schermo per assicurarci di ottenere i risultati attesi. Questo metodo è insufficiente in un contesto professionale. I test dovrebbero sempre essere automatizzati il più possibile e mirare a non richiedere alcun intervento umano. Gli esseri umani sono infatti soggetti a stanchezza e la loro capacità di verificare i test diminuisce nel corso della giornata. Lo strumento [NUnit] aiuta a realizzare questa automazione. È disponibile all'URL [http://www.nunit.org/].
Il progetto Visual Studio per il livello [dao] si evolverà come segue:
![]() |
- in [1], il programma di test [NUnit.cs]
- in [2,3], il progetto genererà una DLL denominata [pam-dao-hibernate.dll]
- in [4], il riferimento alla DLL del framework NUnit: [nunit.framework.dll]
- in [5], la classe [Main.cs] non sarà inclusa nella DLL [pam-dao-hibernate]
- In [6], la classe [NUnit.cs] sarà inclusa nella DLL [pam-dao-hibernate]
La classe di test NUnit <a id="pam-dao-nhibernate-nunit"></a> è 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 {
// the [dao] layer to be tested
private IPamDao pamDao = null;
// manufacturer
public NunitPamDao() {
// layer instantiation [dao]
pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
}
// init
[SetUp]
public void Init() {
}
[Test]
public void GetAllIdentitesEmployes() {
// audit no. of employees
Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
}
[Test]
public void GetCotisations() {
// checking contribution rates
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() {
// individual verification
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() {
// non-existent individual verification
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 che fornisce l'accesso al livello [dao]. Si noti che il tipo di questo campo è un'interfaccia, non una classe. Ciò significa che l'istanza [pamDao] rende accessibili solo i metodi, in particolare quelli dell'interfaccia [IPamDao].
- I metodi testati nella classe sono quelli con l'attributo [Test]. Per tutti questi metodi, il processo di test è il seguente:
- Il metodo con l'attributo [SetUp] viene eseguito per primo. Viene utilizzato per preparare le risorse (connessioni di rete, connessioni al database, ecc.) necessarie per il test.
- Quindi viene eseguito il metodo da testare
- e infine viene eseguito il metodo con l'attributo [TearDown]. Generalmente viene utilizzato per liberare le risorse allocate 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]. Per l'esempio, abbiamo incluso, alle righe 23–26, 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: Test del metodo [GetAllIdentitesEmployes]
- Righe 35–42: Test del metodo [GetCotisations]
- Righe 45–53: Test del metodo [GetEmploye]
- Righe 56–65: Testare il metodo [GetEmploye] quando si verifica un'eccezione.
La compilazione del progetto crea la DLL [pam-dao-nhibernate.dll] nella cartella [bin/Release].
![]() |
La cartella [bin/Release] contiene anche:
- le DLL che fanno parte dei riferimenti del progetto e hanno l'attributo [Local Copy] impostato su true: [Spring.Core, MySql.data, NHibernate, log4net]. Queste DLL sono accompagnate da copie delle DLL che esse stesse 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 esegue questa duplicazione. In fase di esecuzione, viene utilizzato il file [pam-dao-nhibernate.dll.config], non [App.config].
Carichiamo la DLL [pam-dao-nhibernate.dll] utilizzando lo strumento [NUnit-Gui], versione 2.4.6, ed eseguiamo i test:

Come si vede sopra, i test hanno avuto esito positivo.
Esercizio pratico:
Implementare i test per la classe [PamDaoNHibernate] su una macchina.- Utilizzare diversi file di configurazione [App.config] per utilizzare diversi DBMS (Firebird, MySQL, Postgres, SQL Server)
7.3.5. Generazione della DLL del livello [ ] e [dao]
Una volta che la classe [PamDaoNHibernate] è stata scritta e testata, la DLL del livello [dao] verrà generata come segue:
![]() |
- [1], i programmi di test sono esclusi dall'assembly del progetto
- [2,3], configurazione del progetto
- [4], compilazione del progetto
- La DLL viene generata nella cartella [bin/Release] [5]. La aggiungiamo alle DLL già presenti nella cartella [lib] [6]:
![]() |
7.4. Il livello business
Rivediamo l'architettura generale dell'applicazione [SimuPaie]:
![]() |
Partiamo dal presupposto che il livello [dao] sia completo e sia stato incapsulato nella DLL [pam-dao-nhibernate.dll]. Ci concentreremo ora sul livello [business]. Questo livello implementa le regole di business, in questo caso le regole per il calcolo dello stipendio.
7.4.1. Il progetto Visual Studio [ ] per il livello [business]
Il progetto Visual Studio per il livello business potrebbe apparire come segue:
![]() |
- In [1], l'intero progetto è configurato dal file [App.config]
- in [2], il livello [business] è costituito da due cartelle [entities, service]. La cartella [tests] contiene un programma di test da console (Main.cs) e un programma di test NUnit (NUnit.cs).
- In [3] i riferimenti utilizzati dal progetto. Si noti la DLL [pam-dao-hibernate] del livello [DAO] discusso in precedenza.
7.4.2. L'interfaccia [IPamM tier] del livello [business]
Torniamo all'architettura generale dell'applicazione:
![]() |
Quale interfaccia dovrebbe fornire il livello [business] al livello [UI]? Quali sono le possibili interazioni tra questi due livelli? Ricordiamo l'interfaccia web che verrà presentata all'utente:
![]() |
- Quando il modulo viene visualizzato per la prima volta, l'elenco dei dipendenti dovrebbe apparire in [1]. È sufficiente un elenco semplificato (Cognome, Nome, SSN). Il SSN è necessario per accedere a informazioni aggiuntive sul dipendente selezionato (informazioni da 6 a 11).
- Le informazioni da 12 a 15 sono le varie aliquote contributive.
- Le informazioni da 16 a 19 sono le indennità collegate all'indice del dipendente
- Le informazioni da 20 a 24 consistono nelle componenti salariali calcolate in base agli input dell'utente da 1 a 3.
L'interfaccia [IPamMetier] fornita al livello [ui] dal livello [metier] deve soddisfare i requisiti di cui sopra. Esistono molte interfacce possibili. Proponiamo la seguente:
using Pam.Dao.Entites;
using Pam.Metier.Entites;
namespace Pam.Metier.Service {
public interface IPamMetier {
// list of all employee identities
Employe[] GetAllIdentitesEmployes();
// ------- salary calculation
FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés);
}
}
- riga 7: il metodo che popolerà la casella combinata [1]
- riga 10: il metodo che recupererà le informazioni da 6 a 24. Queste informazioni sono state raccolte in un oggetto di tipo [PayrollSheet].
7.4.3. Entità nel livello [business]
La cartella [entities] nel progetto Visual Studio contiene gli oggetti gestiti dalla classe business: [Payroll] e [PayrollItems].
![]() |
La classe [FeuilleS al] incapsula i campi da 6 a 24 del modulo precedente:
using Pam.Dao.Entites;
namespace Pam.Metier.Entites {
public class FeuilleSalaire {
// automatic properties
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: informazioni da 6 a 11 relative al dipendente di cui si sta calcolando lo stipendio e informazioni da 16 a 19 relative alle sue indennità. Si noti che un oggetto [Employee] incapsula un oggetto [Allowances] che rappresenta le indennità del dipendente.
- riga 9: informazioni da 12 a 15
- riga 10: informazioni da 20 a 24
- righe 13–15: il metodo [ToString]
La classe [Elements Salaire] incapsula le informazioni da 20 a 24 del modulo:
namespace Pam.Metier.Entites {
public class ElementsSalaire {
// automatic properties
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 salariali come spiegato nelle regole aziendali descritte nella sezione 3.2.
- riga 4: lo stipendio base del dipendente, calcolato in base al numero di ore lavorate
- riga 5: contributi detratti da questo stipendio base
- righe 6 e 7: indennità da aggiungere allo stipendio base, in base al grado del dipendente e al numero di giorni lavorati
- riga 8: lo stipendio netto da corrispondere
- Righe 12–15: il metodo [ToString] della classe.
7.4.4. Implementazione del livello [business]
![]() |
Implementeremo l'interfaccia [IPamMetier] con due classi:
- [AbstractBasePamMetier], che è una classe astratta in cui implementeremo l'accesso ai dati per l'interfaccia [IPamMetier]. Questa classe avrà un riferimento al livello [dao].
- [PamMetier], una classe derivata da [AbstractBasePamMetier], che implementerà le regole di business dell'interfaccia [IPamMetier]. Non sarà a 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 {
// data access object
public IPamDao PamDao { get; set; }
// list of all employee identities
public Employe[] GetAllIdentitesEmployes() {
return PamDao.GetAllIdentitesEmployes();
}
// an individual employee with benefits
protected Employe GetEmploye(string ss) {
return PamDao.GetEmploye(ss);
}
// contributions
protected Cotisations GetCotisations() {
return PamDao.GetCotisations();
}
// salary calculation
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 nel livello [business].
- 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 [GetAllEmployeIDs] dell'interfaccia [IPamMetier] – utilizza il metodo omonimo nel livello [dao]
- righe 17–19: metodo interno (protetto) [GetEmployee] che chiama il metodo omonimo nel livello [dao] – dichiarato protetto in modo che le classi derivate possano accedervi senza che sia pubblico
- righe 22-24: metodo interno (protetto) [GetContributions] che chiama il metodo omonimo nel livello [dao]
- riga 27: implementazione astratta (attributo abstract) del metodo [GetSalary] 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 {
// wage calculation
public override FeuilleSalaire GetSalaire(string ss, double heuresTravaillées, int joursTravaillés) {
// SS : employee's SS number
// HeuresTravaillées: number of hours worked
// Days worked: number of days worked
// we get the employee back with his benefits
...
// we recover the various contribution rates
...
// salary components are calculated
...
// we return the payslip
return ...;
}
}
}
- riga 7: la classe deriva da [AbstractBasePamMetier] e quindi implementa l'interfaccia [IPamMetier]
- riga 10: il metodo [GetSalary] da implementare
Domanda: Scrivere il codice per il metodo [GetSalaire].
7.4.5. Il test da console per il livello [business]
Rivediamo il progetto Visual Studio per il livello [business]:
![]() |
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 {
// instantiation layer [metier]
IPamMetier pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
// payslip calculations
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()));
}
// break
Console.ReadLine();
}
}
}
- Riga 11: istanziazione Spring del livello [business].
- Righe 13–14: test del metodo [GetSalary] dell'interfaccia [IPamMetier]
- Righe 15–22: Test del metodo [GetSalaire] quando si verifica un'eccezione
Il programma di test utilizza il seguente file di configurazione [ App.config]:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- configuration sections -->
<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>
<!-- spring configuration -->
<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>
<!-- configuration NHibernate -->
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
....
</hibernate-configuration>
<!-- This section contains the log4net configuration settings -->
<!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
avec l'instruction log4net.Config.XmlConfigurator.Configure();
! -->
<log4net>
...
</log4net>
</configuration>
Questo file è identico al file [App.config] utilizzato per il progetto del livello [dao] (vedere la sezione 7.3.2), con le seguenti piccole 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 discusso 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]. Il progetto deve essere configurato come segue:
![]() |
- 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 utilizzando il database descritto nella sezione 6.2 produce il seguente output in console:
- Righe 1-2: Le 2 buste paga richieste
- riga 3: l'eccezione [PamException] causata da un dipendente inesistente.
7.4.6. Test unitari per il livello di business
Il test precedente era visivo: abbiamo verificato sullo schermo che stessimo effettivamente ottenendo i risultati attesi. Passeremo ora ai test NUnit non visivi.
Torniamo al progetto Visual Studio per il progetto [business]:
![]() |
- in [1], il programma di test NUnit
- in [2], il riferimento alla DLL [nunit.framework]
![]() |
- in [3,4], la compilazione del progetto genererà la DLL [pam-metier-dao-nhibernate.dll].
- in [5], il file [NUnit.cs] sarà incluso nell'assembly [pam-metier-dao-nhibernate.dll] ma non [Main.cs] [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 {
// the [metier] layer to test
private IPamMetier pamMetier;
// manufacturer
public NunitTestPamMetier() {
// layer instantiation [dao]
pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
}
[Test]
public void GetAllIdentitesEmployes() {
// audit no. of employees
Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
}
[Test]
public void GetSalaire1() {
// wage sheet calculation
FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
// checks
Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
// non-existent employee payslip
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 che fornisce l'accesso al livello [metier]. Si noti che il tipo di questo campo è un'interfaccia, 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: Testare il metodo [GetAllIdentitesEmployes]
- Righe 29–42: Test del metodo [GetSalaire]
Il progetto sopra riportato genera la DLL [pam-metier.dll] nella cartella [bin/Release].
![]() |
La cartella [bin/Release] contiene anche:
- le DLL che fanno parte dei riferimenti del progetto e hanno l'attributo [Local Copy] impostato su true: [Spring.Core, MySql.data, NHibernate, log4net, pam-dao-nhibernate]. Queste DLL sono accompagnate da copie delle DLL che esse stesse 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].
Carichiamo la DLL [pam-metier-dao-nhibernate.dll] con lo strumento [NUnit-Gui, versione 2.4.6] ed eseguiamo i test:

Come si vede sopra, i test hanno avuto esito positivo.
Esercizio pratico:
Implementare i test per la classe [PamMetier] sul computer.- Utilizzare diversi file di configurazione App.config per utilizzare diversi DBMS (Firebird, MySQL, Postgres, SQL Server)
7.4.7. Generazione della DLL del livello [business]
Una volta che la classe [PamMetier] è stata scritta e testata, genereremo la DLL [pam-metier-dao-hibernate.dll] per il livello [business] seguendo il metodo descritto nella sezione 7.3.5. Faremo attenzione a non includere i programmi di test [Main.cs] e [NUnit.cs] nella DLL. La collocheremo quindi nella cartella [lib] delle DLL [1].
![]() |
7.5. Il livello [web]
Rivediamo l'architettura generale dell'applicazione [SimuPaie]:
![]() |
Partiamo dal presupposto che i livelli [dao] e [business] siano completi e incapsulati nelle DLL [pam-dao-hibernate, pam-business-dao-hibernate.dll]. Descriveremo ora il livello web.
7.5.1. Il progetto Visual Web Developer per il livello [web]
![]() |
- in [1], il progetto nel suo complesso:
- [Global.asax]: la classe istanziata all'avvio dell'applicazione web, che gestisce l'inizializzazione dell'applicazione
- [Default.aspx]: la pagina del modulo web
- in [2], le DLL richieste dall'applicazione web. Si notino le DLL per i livelli [DAO] e [business] compilate 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 [business] discusso in precedenza. Questi dati 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>
<!-- spring configuration -->
<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>
<!-- configuration 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>
<!-- This section contains the log4net configuration settings -->
<!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
avec l'instruction log4net.Config.XmlConfigurator.Configure();
! -->
<log4net>
....
</log4net>
<appSettings/>
<connectionStrings/>
<system.web>
....
....
</configuration>
Le righe 9–12, 18–28 e 31–44 contengono la configurazione di Spring e NHibernate descritta nel file [App.config] del livello [business] (vedere la Sezione 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
{
// --- static application data ---
public static Employe[] Employes;
public static IPamMetier 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 IPamMetier;
// 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;
}
}
}
}
Si noti che:
- la classe [Global.asax.cs] viene istanziata all'avvio dell'applicazione e questa istanza è accessibile a tutte le richieste di tutti gli utenti. I campi statici nelle righe 11-14 sono quindi condivisi tra tutti gli utenti.
- Il metodo [Application_Start] viene eseguito una sola volta dopo l'istanziazione della classe. Questo è il metodo in cui l'applicazione viene tipicamente inizializzata.
I dati condivisi da tutti gli utenti sono i seguenti:
- riga 11: l'array di oggetti [Employee] che memorizzerà l'elenco semplificato (SS, LAST_NAME, FIRST_NAME) di tutti i dipendenti
- riga 12: un riferimento al livello [business] incapsulato nella DLL [pam-metier-dao-nhibernate.dll]
- riga 13: un messaggio che indica se l'inizializzazione è stata completata con successo o con un 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 [business] e [DAO] e restituisce un riferimento al livello [business]. Questo viene memorizzato nel campo statico [PamMetier] della riga 12.
- riga 25: l'array dei dipendenti viene richiesto al livello [business]
- riga 27: il messaggio di successo
- riga 32: il messaggio di errore
7.5.3. Il modulo [Default.a spx]
Il modulo è quello della versione 2.

Domanda: Utilizzando come guida il codice C# della pagina [Default.aspx.cs] della versione 2, scrivi il codice [Default.aspx.cs] per la versione 3. L'unica differenza riguarda il calcolo dello stipendio. Mentre la versione 2 utilizzava l'API ADO.NET per recuperare le informazioni dal database, qui useremo il metodo GetSalaire da [business].
Esercizio pratico:
Distribuisci l'applicazione web precedente su una macchina- utilizza diversi file di configurazione [Web.config] per utilizzare diversi DBMS (Firebird, MySQL, Postgres, SQL Server)





























