Skip to content

13. L'applicazione [SimuPaie] – versione 9 – Integrazione Spring/NHibernate

Qui proponiamo di rivisitare l'applicazione ASP.NET a tre livelli della versione 7 [pam-v7-3tier-nhibernate-multivues-multipages]. L'architettura a livelli dell'applicazione era la seguente:

Nella versione precedente, il livello [DAO] era stato implementato utilizzando il framework NHibernate. Il framework Spring era stato utilizzato solo per integrare i livelli tra loro. Il framework Spring fornisce classi di utilità per lavorare con il framework NHibernate. L'utilizzo di queste classi semplifica la scrittura del codice nel livello [DAO]. L'architettura precedente si evolve come segue:

Grazie alla struttura a livelli utilizzata, l'implementazione dell'integrazione Spring/NHibernate richiede la modifica solo del livello [DAO]. I livelli [Presentation] (web/ASP.NET) e [Business] non dovranno essere modificati. Questo è il vantaggio principale delle architetture a livelli integrate con Spring.

Nelle sezioni seguenti, realizzeremo il livello [DAO] con [Spring/NHibernate] commentando il codice di una soluzione funzionante. Non tenteremo di coprire tutte le opzioni di configurazione o gli utilizzi del framework [Spring/NHibernate]. I lettori possono adattare la soluzione proposta alle proprie esigenze utilizzando la documentazione di Spring.NET [http://www.springframework.net/documentation.html] (giugno 2010).

L'approccio seguito per costruire i livelli [DAO] e [business] è quello della versione 3 descritta nel paragrafo 7. L'approccio seguito per il livello [presentazione] è quello della versione 7 descritta nel paragrafo 11.

13.1. Il livello di accesso ai dati [DAO]

13.1.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
    • la cartella [pam] contiene le classi del progetto e la configurazione delle entità NHibernate
    • i file [App.config] e [Dao.xml] configurano il framework Spring/NHibernate. Dovremo descrivere il contenuto di questi due file.
  • in [2], le varie classi del progetto
    • nella cartella [entities], troviamo le entità NHibernate studiate nel progetto [pam-dao-nhibernate]
    • Nella cartella [service] troviamo l'interfaccia [IPamDao] e la sua implementazione utilizzando il framework Spring/NHibernate [PamDaoSpringNHibernate]. Dovremo scrivere questa nuova implementazione dell'interfaccia [IPamDao]
    • La cartella [tests] contiene gli stessi test del progetto [pam-dao-nhibernate]. Essi testano la stessa interfaccia [IPamDao].
  • In [3], i riferimenti del progetto. L'integrazione Spring/NHibernate richiede due nuove DLL: [Spring.Data] e [Spring.Data.NHibernate12]. Queste DLL sono disponibili nel framework Spring.Net. Sono state aggiunte alla cartella [lib] delle DLL [4]:

Nei riferimenti del progetto [3] troverete le seguenti DLL:

  • NHibernate: per l'ORM NHibernate
  • MySql.Data: il driver ADO.NET per il DBMS MySQL
  • Spring.Core: per il framework Spring, che gestisce l'integrazione dei livelli
  • log4net: una libreria di logging
  • nunit.framework: una libreria per i test unitari
  • Spring.Data e Spring.Data.NHibernate12: forniscono il supporto Spring/NHibernate.

Questi riferimenti sono stati presi dalla cartella [lib] [4]. Assicurati che la proprietà "Copia locale" per tutti questi riferimenti sia impostata su "True" [5]:

13.1.2. Configurazione del progetto C#

Il progetto è configurato come segue:

  • In [1], il nome dell'assembly del progetto è [pam-dao-spring-nhibernate]. Questo nome compare in vari file di configurazione del progetto.

13.1.3. Entità nel livello [dao]

Le entità (oggetti) necessarie per il livello [dao] sono state raccolte nella cartella [entities] [1] del progetto. Queste entità sono le stesse presenti nel progetto [pam-dao-nhibernate], con un'eccezione nei file di configurazione di NHibernate. Prendiamo, ad esempio, il file [Employee.hbm.xml]:

  • In [2], il file è configurato per essere incluso nell'assembly del progetto

Il suo contenuto è il seguente:


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-spring-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="all" lazy="false"/>
    </class>
</hibernate-mapping>
  • Riga 2: L'attributo assembly indica che il file [Employee.hbm.xml] si trova nell'assembly [pam-dao-spring-nhibernate]

13.1.4. Configurazione Spring / NHibernate

Torniamo al progetto Visual C#:

  • In [1], i file [App.config] e [Dao.xml] configurano l'integrazione Spring / NHibernate

13.1.4.1. Il file [ App.config]

Il file [App.config] è il seguente:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- configuration sections -->
    <configSections>
        <sectionGroup name="spring">
            <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
        </sectionGroup>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
    </configSections>
 
 
    <!-- spring configuration -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>
 
    <!-- 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>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%-5level %logger - %message%newline" />
            </layout>
        </appender>
 
        <!-- Set default logging level to DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>
 
        <!-- Set logging for Spring.  Logger names in Spring correspond to the namespace -->
        <logger name="Spring">
            <level value="INFO" />
        </logger>
 
        <logger name="Spring.Data">
            <level value="DEBUG" />
        </logger>
 
        <logger name="NHibernate">
            <level value="DEBUG" />
        </logger>
    </log4net>
 
</configuration>

Il file [App.config] sopra riportato configura Spring (righe 5–9, 15–22) e log4net (riga 10, righe 28–53), ma non NHibernate. Gli oggetti Spring non sono configurati in [App.config] ma nel file [Dao.xml] (riga 20). La configurazione di Spring/NHibernate, che consiste nel dichiarare specifici oggetti Spring, si troverà quindi in questo file.

13.1.4.2. Il file [Dao.xml]

Il file [Dao.xml], che contiene gli oggetti gestiti da Spring, è il seguente:


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
         xmlns:db="http://www.springframework.net/database">
 
    <!-- Referenced by main application context configuration file -->
    <description>
        Application Spring / NHibernate
    </description>
 
    <!-- Database and NHibernate Configuration -->
    <db:provider id="DbProvider"
                   provider="MySql.Data.MySqlClient"
                   connectionString="Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;"/>
 
    <object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="MappingAssemblies">
            <list>
                <value>pam-dao-spring-nhibernate</value>
            </list>
        </property>
        <property name="HibernateProperties">
            <dictionary>
                <entry key="hibernate.dialect" value="NHibernate.Dialect.MySQLDialect"/>
                <entry key="hibernate.show_sql" value="false"/>
            </dictionary>
        </property>
        <property name="ExposeTransactionAwareSessionFactory" value="true" />
    </object>
 
    <!-- transaction manager -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>
 
    <!-- Hibernate Template -->
    <object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
        <property name="SessionFactory" ref="NHibernateSessionFactory" />
        <property name="TemplateFlushMode" value="Auto" />
        <property name="CacheQueries" value="true" />
    </object>
 
    <!-- Data Access Objects -->
    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
    </object>
</objects>
  • Le righe 11–13 configurano la connessione al database [dbpam_nhibernate]. Esse includono:
    • il provider ADO.NET necessario per la connessione, in questo caso il provider DBMS MySQL. Ciò richiede che la DLL [Mysql.Data] sia inclusa nei riferimenti del progetto.
    • la stringa di connessione al database (server, nome del database, proprietario della connessione e password)
  • Le righe 15–29 configurano la SessionFactory di NHibernate, l'oggetto utilizzato per ottenere le sessioni NHibernate. Si noti che tutte le operazioni sul database vengono eseguite all'interno di una sessione NHibernate. Alla riga 15, si può notare che la SessionFactory è implementata dalla classe Spring.Data.NHibernate.LocalSessionFactoryObject, presente nella DLL Spring.Data.NHibernate12.
  • Riga 16: la proprietà DbProvider imposta i parametri di connessione al database (provider ADO.NET e stringa di connessione). In questo caso, questa proprietà fa riferimento all'oggetto DbProvider definito in precedenza alle righe 11-13.
  • Righe 17-20: specificano l'elenco degli assembly contenenti i file [*.hbm.xml] che configurano le entità gestite da NHibernate. La riga 19 indica che questi file si trovano nell'assembly del progetto. Si noti che questo nome si trova nelle proprietà del progetto C#. Si noti inoltre che tutti i file [*.hbm.xml] sono stati configurati per essere inclusi nell'assembly del progetto.
  • Righe 22–27: Proprietà specifiche di NHibernate.
    • Riga 24: il dialetto SQL utilizzato sarà MySQL
    • Riga 25: L'SQL generato da NHibernate non apparirà nei log della console. Impostando questa proprietà su true è possibile visualizzare le istruzioni SQL generate da NHibernate. Ciò può aiutare a comprendere, ad esempio, perché un'applicazione è lenta durante l'accesso al database.
  • Riga 28: Impostare la proprietà ExposeTransactionAwareSessionFactory su true farà sì che Spring gestisca le annotazioni di gestione delle transazioni presenti nel codice C#. Torneremo su questo argomento quando scriveremo la classe che implementa il livello [DAO].
  • Le righe 32–36 definiscono il gestore delle transazioni. Anche in questo caso, si tratta di una classe Spring proveniente dalla DLL Spring.Data.NHibernate12. Questo gestore richiede i parametri di connessione al database (riga 34) e la SessionFactory di NHibernate (riga 35).
  • Le righe 39–43 definiscono le proprietà della classe HibernateTemplate, che è anch'essa una classe Spring. Questa classe verrà utilizzata come classe di utilità nella classe che implementa il livello [DAO]. Facilita le interazioni con gli oggetti NHibernate. Questa classe ha alcune proprietà che devono essere inizializzate:
    • riga 40: la SessionFactory di NHibernate
    • riga 41: la proprietà TemplateFlushMode imposta la modalità di sincronizzazione del contesto di persistenza NHibernate con il database. La modalità Auto garantisce che la sincronizzazione avvenga:
      • alla fine di una transazione
      • prima di un'operazione SELECT
    • riga 42: le query HQL (Hibernate Query Language) verranno memorizzate nella cache. Ciò può comportare un aumento delle prestazioni.
  • Le righe 46–48 definiscono la classe di implementazione per il livello [dao]
    • riga 46: il livello [dao] sarà implementato dalla classe [PamdaoSpringNHibernate] nella DLL [pam-dao-spring-nhibernate]. Dopo l'istanziazione della classe, verrà eseguito immediatamente il metodo init della classe. Quando il contenitore Spring viene chiuso, verrà eseguito il metodo destroy della classe.
    • Riga 47: la classe [PamDaoSpringNHibernate] avrà una proprietà HibernateTemplate che verrà inizializzata con la proprietà HibernateTemplate della riga 39.

13.1.5. Implementazione del livello [dao]

13.1.5.1. Lo scheletro della classe di implementazione

L'interfaccia [IPamDao] è la stessa del progetto [pam-dao-nhibernate]:


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 degli operatori dell'assistenza all'infanzia in un formato semplificato (cognome, nome, codice previdenziale).
    • 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.

Lo scheletro della classe di implementazione per questa interfaccia utilizzando Spring/NHibernate potrebbe essere il seguente:


using System;
using System.Collections;
using System.Collections.Generic;
using Pam.Dao.Entites;
using Spring.Data.NHibernate.Generic.Support;
using Spring.Transaction.Interceptor;
 
namespace Pam.Dao.Service {
    public class PamDaoSpringNHibernate : HibernateDaoSupport, IPamDao {
        // private fields 
        private Cotisations cotisations;
        private Employe[] employes;
 
        // init 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }
 
        // delete object
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }
 
        // list of all employee identities
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }
 
 
        // an individual employee with benefits 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }
 
        // list of contributions 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}
  • Riga 9: La classe [PamDaoSpringNHibernate] implementa correttamente l'interfaccia del livello [dao] [IPamDao]. Deriva inoltre dalla classe Spring [HibernateDaoSupport]. Questa classe dispone di una proprietà [HibernateTemplate] che viene inizializzata dalla configurazione Spring impostata (riga 2 di seguito):

    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
  • Nella riga 1 sopra, vediamo che la definizione dell'oggetto [pamdao] specifica che i metodi init e destroy della classe [PamDaoSpringNHibernate] devono essere eseguiti in momenti specifici. Questi due metodi sono effettivamente presenti nella classe alle righe 16 e 21.
  • Righe 15, 34: annotazioni che assicurano che il metodo annotato venga eseguito all'interno di una transazione. L'attributo ReadOnly=true indica che la transazione è di sola lettura. Il metodo eseguito all'interno della transazione può generare un'eccezione. In questo caso, Spring esegue automaticamente il rollback della transazione. Questa annotazione elimina la necessità di gestire una transazione all'interno del metodo.
  • Riga 16: il metodo init viene eseguito da Spring immediatamente dopo l'istanziazione della classe. Vedremo che il suo scopo è quello di inizializzare i campi privati alle righe 11 e 12. Verrà eseguito all'interno di una transazione (riga 15).
  • I metodi dell'interfaccia [IPamDao] sono implementati alle righe 28, 35 e 40.
  • Righe 28–30: il metodo [GetAllIdentitesEmployes] restituisce semplicemente l'attributo della riga 12, che è stato inizializzato dal metodo init.
  • Righe 40–42: il metodo [GetCotisations] restituisce semplicemente l'attributo della riga 11, che è stato inizializzato dal metodo init.

13.1.5.2. Metodi utili della classe HibernateTemplate

Utilizzeremo i seguenti metodi della classe HibernateTemplate:

IList<T> Find<T>(string hql_query)
esegue la query HQL e restituisce un elenco di oggetti di tipo T
IList<T> Find<T>(string hql_query, object[])
esegue una query HQL parametrizzata da ?. I valori di questi parametri sono forniti dall'array di oggetti.
IList<T> LoadAll<T>()
restituisce tutte le entità di tipo T
  

Esistono altri metodi utili che non avremo modo di utilizzare, che consentono di recuperare, salvare, aggiornare ed eliminare entità:

T Load<T>(object id)
Aggiunge l'entità di tipo T con chiave primaria id alla sessione NHibernate.
void SaveOrUpdate(object entity)
Inserisce (INSERT) o aggiorna (UPDATE) l'oggetto entità a seconda che abbia una chiave primaria (UPDATE) o meno (INSERT). L'assenza di una chiave primaria può essere configurata tramite l'attributo `unsaved-values` nel file di configurazione dell'entità. Dopo l'operazione `SaveOrUpdate`, l'oggetto entità si trova nella sessione NHibernate.
void Delete(object entity)
Rimuove l'oggetto entità dalla sessione NHibernate.

13.1.5.3. Implementazione del metodo init

Il metodo init della classe [PamDaoSpringNHibernate] è, per impostazione predefinita, il metodo eseguito dopo che Spring ha istanziato la classe. Il suo scopo è quello di memorizzare nella cache locale le identità semplificate dei dipendenti (cognome, nome, SSN) e le aliquote contributive. Il codice potrebbe essere il seguente.


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // on récupère la liste simplifiée des employés
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // on la met dans un tableau
                employes = new Employe[lignes.Count];
                int i = 0;
                foreach (object[] ligne in lignes) {
                    employes[i] = new Employe() { SS = ligne[0].ToString(), Nom = ligne[1].ToString(), Prenom = ligne[2].ToString() };
                    i++;
                }
                // on met les taux de cotisations dans un objet 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // on transforme l'exception 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • riga 5: Viene eseguita una query HQL. Recupera i campi SS, LastName e FirstName da tutte le entità Employee. Restituisce un elenco di oggetti. Se avessimo richiesto l'intero record Employee utilizzando "select e from Employee e", avremmo recuperato un elenco di oggetti di tipo Employee.
  • Righe 7–12: Questo elenco di oggetti viene copiato in un array di oggetti di tipo Employee.
  • Riga 14: Recuperiamo l'elenco di tutte le entità di tipo Contributions. Sappiamo che questo elenco ha un solo elemento. Recuperiamo quindi il primo elemento dell'elenco per ottenere i tassi di contribuzione.
  • Le righe 7 e 14 inizializzano i due campi privati della classe.

13.1.5.4. Implementazione del metodo GetEmployee

Il metodo GetEmployee deve restituire l'entità Employee con un dato SSN. Il suo codice potrebbe essere il seguente:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                // requête
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // on transforme l'exception 
                throw new PamException(string.Format("Erreur d'accès à la BD lors de la demande de l'employé de n° ss [{0}] : [{1}]", ss, ex.ToString()), 41);
            }
            // a-t-on récupéré un employé ? 
            if (employés.Count == 0) {
                // on signale le fait 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • Riga 6: recupera l'elenco dei dipendenti con un determinato numero di previdenza sociale
  • riga 12: normalmente, se il dipendente esiste, dovremmo ottenere un elenco con un solo elemento
  • riga 14: in caso contrario, genera un'eccezione
  • riga 16: in tal caso, restituisce il primo dipendente dell'elenco

13.1.5.5. Conclusione

Se confrontiamo il codice del livello [DAO] quando si utilizzano

  1. il framework Spring / NHibernate
  2. il framework Spring / NHibernate

ci rendiamo conto che la seconda soluzione ha permesso di ottenere un codice più semplice.

13.2. Test del livello [DAO]

13.2.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 [tests] contiene un test da console [Main.cs] e un test unitario [NUnit.cs].
  • in [3], il programma [Main.cs] viene compilato.
  • in [4], il file [NUnit.cs] non viene generato.
  • Il progetto è un'applicazione console. La classe in esecuzione è quella specificata in [5], ovvero la classe nel file [Main.cs].

13.2.2. Il programma di test da console [Main.cs]

Il programma di test [Main.cs] viene eseguito con la seguente struttura:

È 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 tramite il file [ App.config] descritto nella Sezione 13.1.4.1.

L'esecuzione del codice con 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

13.2.3. Test unitari con NUnit

Passiamo ora a un test unitario con NUnit. 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-spring-nhibernate.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-spring-nhibernate]
  • in [6], la classe [NUnit.cs] sarà inclusa nella DLL [pam-dao-spring-nhibernate]

La classe di test NUnit è la seguente:


using System.Collections;
using NUnit.Framework;
using Pam.Dao.Service;
using Pam.Dao.Entites;
using Spring.Objects.Factory.Xml;
using Spring.Core.IO;
using Spring.Context.Support;
 
namespace Pam.Dao.Tests {
 
    [TestFixture]
    public class NunitPamDao : AssertionHelper {
        // 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);
        }
    }
}

Questa classe è già stata trattata nella Sezione 7.3.4.

La generazione del progetto crea la DLL [pam-dao-spring-nhibernate.dll] nella cartella [bin/Release].

Carichiamo la DLL [pam-dao-spring-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 [PamDaoSpringNHibernate] su un computer.
  • Utilizzare diversi file di configurazione [Dao.xml] per utilizzare altri DBMS (Firebird, MySQL, Postgres, SQL Server)

13.2.4. Generazione della DLL del livello [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]:

13.3. 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-spring-nhibernate.dll]. Ci concentreremo ora sul livello [business]. Questo è il livello che implementa le regole di business, in questo caso le regole per il calcolo dello stipendio.

Il progetto Visual Studio per il livello business potrebbe presentarsi come segue:

  • In [1], l'intero progetto è configurato dai file [App.config] e [Dao.xml]. Il file [App.config] è identico a quello presente nel progetto del livello [dao] [pam-dao-spring-nhibernate]. Lo stesso vale per il file [Dao.xml], tranne per il fatto che dichiara un oggetto Spring aggiuntivo con l'ID pammetier. La dichiarazione di questo oggetto è identica a quella presente nel file [App.config] del progetto [pam-metier-dao-nhibernate].
  • In [2], la cartella [pam] è identica a quella presente nel livello [metier] del progetto [pam-metier-dao-nhibernate]
  • In [3], i riferimenti utilizzati dal progetto. Si noti la DLL [pam-dao-spring-nhibernate] del livello [dao] discussa in precedenza.

Domanda: Compilare il progetto [pam-metier-dao-spring-nhibernate] sopra riportato. Verrà testato separatamente:

  • in modalità console dal programma console [Main.cs]

  • tramite il test unitario [NUnit.cs] eseguito dal framework NUnit

Il nuovo progetto [pam-metier-dao-spring-nhibernate] può essere compilato semplicemente copiando il progetto [pam-metier-dao-nhibernate] e modificando poi gli elementi che devono essere cambiati.


Dopo il test, genereremo la DLL del livello [business], che chiameremo [pam-metier-dao-spring-nhibernate]:

  • in [1], il test NUnit riuscito
  • in [2], la DLL generata dal progetto

Aggiungeremo la DLL del livello [business] alle DLL già presenti nella cartella [lib] [3]:

13.4. 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-spring-nhibernate, pam-business-dao-spring-nhibernate]. Descriveremo ora il livello web.

Il progetto Visual Web Developer per il livello [web] si ottiene innanzitutto semplicemente copiando la cartella del progetto web [pam-v7-3tier-nhibernate-multivues-multipages]. Il progetto viene quindi rinominato [pam-v9-3tier-spring-nhibernate-multivues-multipages]:

Il nuovo progetto web [pam-v9-3tier-spring-nhibernate-multivues-multipages] differisce dal progetto [pam-v7-3tier-nhibernate-multivues-multipages] nei seguenti modi:

  • In [1], la configurazione avviene tramite i file [Dao.xml] e [Web.config]. Il file [Dao.xml] non era presente in [pam-v7] e il file [Web.config] deve includere la configurazione di Spring/NHibernate, mentre in [pam-v7] era configurato solo NHibernate.
  • In [2], le DLL per i livelli [dao] e [business] sono quelle che abbiamo appena creato.

Il file [Dao.xml] è quello utilizzato per la creazione del livello [business]. Il file [Web.config] è quello di [pam-v7], al quale aggiungiamo la configurazione Spring/NHibernate presente nei file [App.config] dei livelli [dao] e [business]. Il file [Web.config] per [pam-v9] è il seguente:


<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="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
    </sectionGroup>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>
 
  <!-- spring configuration -->
  <spring>
    <parsers>
      <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
    </parsers>
    <context>
      <resource uri="~/Dao.xml" />
    </context>
  </spring>
............. le reste est identique au fichier [Web.config] de [pam-v7]

Le righe 7–11 e 16–23 contengono la configurazione Spring che era presente nei file [App.config] dei livelli [dao] e [business] creati in precedenza, con una differenza: nei file [App.config], la riga 17 era scritta come segue:


      <resource uri="Dao.xml" />

Con la seguente configurazione:

Il file [Dao.xml] viene copiato nella cartella [bin] all'interno della cartella del progetto web. Con la sintassi


      <resource uri="Dao.xml" />

il file [Dao.xml] verrà cercato nella directory corrente del processo che esegue l'applicazione web. Si scopre che questa directory non è la cartella [bin] del progetto web in esecuzione. È necessario scrivere:


      <resource uri="~/Dao.xml" />

in modo che il file [Dao.xml] venga cercato nella cartella [bin] del progetto web in esecuzione.


Domanda: Distribuire questa applicazione web su una macchina.


13.5. Conclusione

Siamo passati dall'architettura:

all'architettura:

L'obiettivo era implementare il livello [DAO] sfruttando le funzionalità offerte dall'integrazione di Spring con NHibernate.

Abbiamo osservato che ciò:

  • influiva sul livello [DAO]. Questo livello era più semplice da scrivere ma richiedeva una configurazione di Spring più complessa.
  • influiva marginalmente sui livelli [business] e [web]

Questo ha fornito un altro esempio dei vantaggi delle architetture a livelli.