Skip to content

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:

  1. Il client invia una richiesta all'applicazione.
  2. 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.
  3. 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:

  1. 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
  1. 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].
  2. 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.
  3. 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].
  4. 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.

  1. Una volta ottenuti tutti i dati necessari, il livello [business] calcola la retribuzione della persona selezionata dall’utente.
  2. Il livello [business] può ora rispondere alla richiesta del livello [ui] effettuata in (d). Questo è il percorso [7].
  3. Il livello [ui] formatterà questi risultati per presentarli all'utente in una forma appropriata e poi li visualizzerà. Questo è il percorso [8].
  4. 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:
throw new PamException("Problème d'accès aux données",5);
  • (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:

1
2
3
4
5
6
[254104940426058,Jouveinal,Marie,,,,]
[260124402111742,Laverti,Justine,,,,]
------------------------------------
[254104940426058,Jouveinal,Marie,5 rue des oiseaux,St Corentin,49203,[2, 2,1, 2,1, 3,1, 15]]
------------------------------------
[3,49,6,15,9,39,7,88]
  • 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:

Image

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:

  1. 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).
  2. Le informazioni da 12 a 15 sono le varie aliquote contributive.
  3. Le informazioni da 16 a 19 sono le indennità collegate all'indice del dipendente
  4. 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:

1
2
3
[[260124402111742,Laverti,Justine,La Brûlerie,St Marcel,49014,[1, 1,93, 2, 3, 12]],[3,49,6,15,9,39,7,88],[1, 1,93, 2, 3, 12],[64,85 : 17,45 : 10 : 15 : 72,4 ]
[[254104940426058,Jouveinal,Marie,5 rue des oiseaux,St Corentin,49203,[2, 2,1, 2,1, 3,1, 15]],[3,49,6,15,9,39,7,88],[2, 2,1, 2,1, 3,1, 15],[362,25 : 97,48 : 42: 62 : 368,77 ]
PamException : L'employé de n° ss [xx] n'existe pas
  • 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:

Image

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.

Image


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)