Skip to content

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

In questa sede ci proponiamo di riprendere l’applicazione ASP.NET a tre livelli dalla versione 7 [pam-v7-3tier-nhibernate-multivues-multipages]. L’architettura a livelli dell’applicazione era la seguente:

In precedenza, il livello [dao] era stato implementato con il framework NHibernate. Il framework Spring era stato utilizzato solo per l’integrazione tra i vari livelli. Il framework Spring offre classi di utilità per lavorare con il framework Nhibernate. L’utilizzo di queste classi semplifica la scrittura del codice del livello [dao]. L’architettura precedente si evolve come segue:

A causa della struttura a livelli utilizzata, l’utilizzo dell’integrazione Spring / NHibernate comporta la modifica del solo livello [dao]. I livelli [presentation] (web / ASP.NET) e [metier] non dovranno essere modificati. Questo è il principale vantaggio delle architetture a livelli integrate da Spring.

Di seguito, realizzeremo il livello [dao] insieme a [Spring / NHibernate], commentando il codice di una soluzione funzionante. Non cercheremo di illustrare tutte le possibilità di configurazione o di utilizzo del framework [Spring / Nhibernate]. Il lettore potrà adattare la soluzione proposta alle proprie esigenze avvalendosi della documentazione relativa a Spring.NET e [http://www.springframework.net/documentation.html] (giugno 2010).

L’approccio seguito per la realizzazione dei livelli [dao] e [metier] è quello della versione 3 descritto al paragrafo 7. Quello seguito per il livello [présentation] è quello della versione 7 descritto al paragrafo 11.

13.1. Il livello [dao] di accesso ai dati

13.1.1. Il progetto Visual Studio C# del livello [dao]

Il progetto Visual Studio del livello [dao] è il seguente:

  • in [1], il progetto nella sua interezza
    • 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] sono presenti le diverse classi del progetto
    • nella cartella [entites] troviamo le entità NHibernate analizzate nel progetto [pam-dao-nhibernate]
    • nella cartella [service] troviamo l'interfaccia [IPamDao] e la sua implementazione con 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 verificano la stessa interfaccia [IPamdao].
  • In [3], i riferimenti del progetto. L’integrazione Spring / NHibernate richiede due nuovi file: DLL, [Spring.Data] e [Spring.Data.NHibernate12]. Questi DLL sono disponibili nel framework Spring.Net. Sono stati aggiunti alla cartella [lib] dei DLL e [4]:

Nei riferimenti [3] del progetto si trovano i seguenti DLL:

  • NHibernate: per ORM NHibernate
  • MySql.Data: il driver ADO.NET del SGBD MySQL
  • Spring.Core: per il framework Spring che garantisce l'integrazione dei livelli
  • log4net: una libreria di log
  • nunit.framework: una libreria di test unitari
  • Spring.Data e Spring.Data.NHibernate12: garantiscono il supporto di Spring / NHibernate.

Questi riferimenti sono stati presi dalla cartella [lib] [4]. È necessario assicurarsi che per tutti questi riferimenti la proprietà "Copia locale" 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. Le entità del livello [dao]

Le entità (oggetti) necessarie per il livello [dao] sono state raccolte nella cartella [entites] [1] del progetto. Queste entità sono quelle del progetto [pam-dao-nhibernate], con una sola differenza nei file di configurazione NHibernate. Prendiamo ad esempio il file [Employe.hbm.xml]:

  • in [2], il file è configurato per essere incorporato 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 [Employe.hbm.xml] si troverà 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>
    <!-- sezioni di configurazione -->
    <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>


    <!-- Configurazione Spring -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>

    <!-- Questa sezione contiene le impostazioni di configurazione di log4net -->
    <!-- NOTE IMPORTANTE: i log non sono attivi per impostazione predefinita. È necessario attivarli tramite il programma
    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>

        <!-- Impostare il livello di registrazione predefinito su DEBUG -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>

        <!-- Imposta la registrazione per Spring.  I nomi dei logger in Spring corrispondono allo spazio dei nomi -->
        <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), log4net (riga 10, righe 28-53) ma non NHibernate. Gli oggetti Spring non sono configurati nel file [App.config], ma nel file [Dao.xml] (riga 20). La configurazione di Spring / NHibernate, che consiste nel dichiarare oggetti Spring specifici, si troverà quindi in questo file.

13.1.4.2. Il file [Dao.xml]

Il file [Dao.xml], che raccoglie 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">

    <!-- Riferito dal file di configurazione del contesto dell’applicazione principale -->
    <description>
        Application Spring / NHibernate
    </description>

    <!-- Configurazione del database e di NHibernate -->
    <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>

    <!-- Gestore delle transazioni -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>

    <!-- Modello Hibernate -->
    <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>

    <!-- Oggetti di accesso ai dati -->
    <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]. Vi si trova:
    • il provider ADO.NET necessario per la connessione, in questo caso il provider di SGBD MySQL. Ciò implica che nei riferimenti del progetto siano presenti DLL e [Mysql.Data].
    • la stringa di connessione al database (server, nome del database, proprietario della connessione, password)
  • le righe 15-29 configurano il SessionFactory di NHibernate, l'oggetto che serve per ottenere le sessioni NHibernate. Si ricorda che ogni operazione sul database viene eseguita all’interno di una sessione NHibernate. Alla riga 15 si può notare che SessionFactory è implementata dalla classe Spring Spring.Data.NHibernate.LocalSessionFactoryObject, presente in 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, tale proprietà fa riferimento all’oggetto DbProvider definito in precedenza alle righe 11-13.
  • righe 17-20: definiscono 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. Ricordiamo che questo nome si trova nelle proprietà del progetto C#. Ricordiamo inoltre che tutti i file [*.hbm.xml] sono stati configurati per essere incorporati nell’assembly del progetto.
  • righe 22-27: proprietà specifiche di NHibernate.
    • riga 24: il dialetto SQL utilizzato sarà quello di MySQL
    • riga 25: il SQL generato da NHibernate non apparirà nei log della console. Impostando questa proprietà su true è possibile conoscere i comandi SQL emessi da NHibernate. Ciò può aiutare a capire, ad esempio, perché un'applicazione è lenta durante l'accesso al database.
  • riga 28: l’impostazione della proprietà da ExposeTransactionAwareSessionFactory a true farà sì che Spring gestisca le annotazioni relative alla gestione delle transazioni che si troveranno 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, tale gestore è una classe Spring della DLL Spring.Data.NHibernate12. Questo gestore deve conoscere i parametri di connessione al database (riga 34) e il SessionFactory di NHibernate (riga 35).
  • Le righe 39-43 definiscono le proprietà della classe HibernateTemplate, anch’essa una classe di Spring. Questa classe verrà utilizzata come classe di utilità nella classe che implementa il livello [dao]. Essa facilita le interazioni con gli oggetti NHibernate. Questa classe presenta alcune proprietà che devono essere inizializzate:
    • riga 40: SessionFactory di NHibernate
    • riga 41: la proprietà TemplateFlushMode imposta la modalità di sincronizzazione del contesto di persistenza NHibernate con il database. La modalità Auto fa sì che la sincronizzazione avvenga:
      • al termine di una transazione
      • prima di un'operazione SELECT
    • riga 42: le query HQL (Hibernate Query Language) verranno memorizzate nella cache. Ciò può comportare un miglioramento delle prestazioni.
  • le righe 46-48 definiscono la classe di implementazione del livello [dao]
    • riga 46: il livello [dao] verrà implementato dalla classe [PamdaoSpringNHibernate] di DLL [pam-dao-spring-nhibernate]. Dopo l’istanziazione della classe, verrà immediatamente eseguito il metodo init della classe. Alla chiusura del contenitore Spring, 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 {
        // Elenco di tutte le identità dei dipendenti 
        Employe[] GetAllIdentitesEmployes();
        // un dipendente specifico con le relative indennità 
        Employe GetEmploye(string ss);
        // elenco di tutti i contributi 
        Cotisations GetCotisations();
    }
}
  • riga 1: si importa lo spazio dei nomi delle entità del livello [dao].
  • riga 3: il livello [dao] si trova nello spazio dei nomi [Pam.Dao.Service]. Gli elementi dello spazio dei nomi [Pam.Dao.Entites] possono essere creati in più esemplari. Gli elementi dello spazio dei nomi [Pam.Dao.Service] vengono creati in un unico esemplare (singleton). È questo 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 delle assistenti materne in forma semplificata (cognome, nome, SS).
    • riga 8, [GetEmploye] restituisce un oggetto [Employe]: il dipendente con il numero di previdenza sociale passato come parametro al metodo, con le indennità relative al suo indice.
    • riga 10, [GetCotisations] restituisce l’oggetto [Cotisations] che incapsula le aliquote dei diversi contributi sociali da prelevare dallo stipendio lordo.

Lo scheletro della classe di implementazione di questa interfaccia con il supporto 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 {
        // campi privati 
        private Cotisations cotisations;
        private Employe[] employes;

        // inizializzazione 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }

        // eliminazione oggetto
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }

        // elenco di tutte le identità dei dipendenti
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }


        // un dipendente specifico con le relative indennità 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }

        // elenco dei contributi 
        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 possiede una proprietà [HibernateTemplate] che viene inizializzata dalla configurazione Spring effettuata (riga 2 qui sotto):

    <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 riportata, si vede che la definizione dell’oggetto [pamdao] indica 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 fanno sì 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 tal caso, Spring esegue automaticamente un 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 ha lo scopo di inizializzare i campi privati delle righe 11 e 12. Si svolgerà 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] si limita a restituire l’attributo della riga 12 inizializzato dal metodo init.
  • righe 40-42: il metodo [GetCotisations] si limita a restituire l'attributo della riga 11 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 requete_hql)
esegue la query HQL e restituisce un elenco di oggetti di tipo T
IList<T> Find<T>(string requete_hql, object[])
esegue una query HQL con parametri contrassegnati 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)
inserisce nella sessione NHibernate l'entità di tipo T con chiave primaria id.
void SaveOrUpdate(object entità)
inserisce (INSERT) o aggiorna (UPDATE) l'oggetto entité a seconda che questo abbia o meno una chiave primaria (UPDATE) (INSERT). L'assenza di una chiave primaria può essere configurata tramite l'attributo unsaved-values del file di configurazione dell'entità. Dopo l'operazione SaveOrUpdate, l'oggetto entité si trova nella sessione NHibernate.
void Delete(object entità)
elimina 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 l'istanziazione della classe da parte di Spring. Il suo scopo è quello di memorizzare nella cache locale le identità semplificate dei dipendenti (cognome, nome, SS) e le aliquote contributive. Il codice potrebbe essere il seguente.


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // si recupera l'elenco semplificato dei dipendenti
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // la si inserisce in una tabella
                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++;
                }
                // si inseriscono le aliquote contributive in un oggetto 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                // si gestisce l'eccezione 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • riga 5: viene eseguita una query HQL. Essa richiede i campi SS, Cognome, Nome di tutte le entità Employé. Restituisce un elenco di oggetti. Se fosse stato richiesto l’intero record del dipendente con la sintassi "select e from Employe e", si sarebbe ottenuto un elenco di oggetti di tipo Employe.
  • righe 7-12: questo elenco di oggetti viene copiato in un array di oggetti di tipo Employe.
  • riga 14: si richiede l’elenco di tutte le entità di tipo Cotisations. Si sa che questo elenco contiene un solo elemento. Si recupera quindi il primo elemento dell’elenco per ottenere le aliquote contributive.
  • Le righe 7 e 14 inizializzano i due campi privati della classe.

13.1.5.4. Implementazione del metodo GetEmploye

Il metodo GetEmploye deve restituire l’entità Dipendente con un determinato numero SS. Il suo codice potrebbe essere il seguente:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                // richiesta
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // si converte l'eccezione 
                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);
            }
            // È stato assunto un dipendente? 
            if (employés.Count == 0) {
                // si segnala il fatto 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • riga 6: ottiene l’elenco dei dipendenti con un determinato n. SS
  • riga 12: normalmente, se il dipendente esiste, si dovrebbe ottenere un elenco con un solo elemento
  • riga 14: in caso contrario, si genera un'eccezione
  • riga 16: in caso contrario, si restituisce il primo dipendente dell’elenco

13.1.5.5. Conclusion

Se si confronta il codice del livello [dao] nel caso di utilizzo

  1. del solo framework NHibernate
  2. del framework Spring / NHibernate

ci si rende conto che la seconda soluzione ha permesso di scrivere un codice più semplice.

13.2. Test del livello [dao]

13.2.1. Il progetto Visual Studio

Il progetto Visual Studio è già stato presentato. Ricordiamolo:

  • in [1], il progetto nella sua interezza
  • in [2], le diverse classi del progetto. La cartella [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 da console. La classe eseguita è quella specificata in [5], ovvero la classe del file [Main.cs].

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

Il programma di test [Main.cs] viene eseguito nella seguente architettura:

Ha il compito di testare i metodi dell'interfaccia [IPamDao]. Un esempio di base potrebbe essere il seguente:


using System;
using Pam.Dao.Entites;
using Pam.Dao.Service;
using Spring.Context.Support;

namespace Pam.Dao.Tests {
    public class MainPamDaoTests {
        public static void Main() {
            try {
                // istanza del livello [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // elenco delle identità dei dipendenti 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // un dipendente con le relative indennità 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // elenco dei contributi 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                // visualizzazione eccezione 
                Console.WriteLine(ex.ToString());
            }
            //pausa 
            Console.ReadLine();
        }
    }
}
  • riga 11: si richiede a Spring un riferimento al livello [dao].
  • righe 13-15: test del metodo [GetAllIdentitesEmployes] dell’interfaccia [IPamDao]
  • riga 18: test del metodo [GetEmploye] dell’interfaccia [IPamDao]
  • riga 21: test del metodo [GetCotisations] dell'interfaccia [IPamDao]

Spring, NHibernate e log4net sono configurati dal file [App.config] analizzato nel paragrafo 13.1.4.1.

L'esecuzione effettuata con il database descritto al paragrafo 6.2 fornisce il seguente risultato in 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 [Employe] con le sole informazioni relative a [SS, Nom, Prenom]
  • riga 4: il dipendente di tipo [Employe] con il numero di previdenza sociale [254104940426058]
  • riga 5: le aliquote contributive

13.2.3. Test unitari con NUnit

Passiamo ora a un test unitario su NUnit. Il progetto Visual Studio del livello [dao] subirà le seguenti modifiche:

  • in [1], il programma di test [NUnit.cs]
  • in [2,3], il progetto genererà un DLL denominato [pam-dao-spring-nhibernate.dll]
  • in [4], il riferimento a DLL del framework NUnit: [nunit.framework.dll]
  • in [5], la classe [Main.cs] non sarà inclusa nella DLL [pam-dao-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 {
        // il livello [dao] da testare 
        private IPamDao pamDao = null;

        // costruttore 
        public NunitPamDao() {
            // istanziazione del livello [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }

        // inizializzazione 
        [SetUp]
        public void Init() {

        }

        [Test]
        public void GetAllIdentitesEmployes() {
            // verifica del numero di dipendenti 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetCotisations() {
            // verifica aliquota contributiva 
            Cotisations cotisations = pamDao.GetCotisations();
            Expect(3.49, EqualTo(cotisations.CsgRds).Within(1E-06));
            Expect(6.15, EqualTo(cotisations.Csgd).Within(1E-06));
            Expect(9.39, EqualTo(cotisations.Secu).Within(1E-06));
            Expect(7.88, EqualTo(cotisations.Retraite).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites() {
            // verifica persone fisiche 
            Employe employe1 = pamDao.GetEmploye("254104940426058");
            Employe employe2 = pamDao.GetEmploye("260124402111742");
            Expect("Jouveinal", EqualTo(employe1.Nom));
            Expect(2.1, EqualTo(employe1.Indemnites.BaseHeure).Within(1E-06));
            Expect("Laverti", EqualTo(employe2.Nom));
            Expect(1.93, EqualTo(employe2.Indemnites.BaseHeure).Within(1E-06));
        }

        [Test]
        public void GetEmployeIdemnites2() {
            // verifica persona inesistente 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}

Questa classe è già stata trattata nel paragrafo 7.3.4.

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

Si carica il file DLL [pam-dao-spring-nhibernate.dll] con lo strumento [NUnit-Gui], versione 2.4.6, e si eseguono i test:

Image

Come si vede sopra, i test hanno avuto esito positivo.

Esercitazione pratica:


  • eseguire sul computer i test della classe [PamDaoSpringNHibernate].
  • utilizzare diversi file di configurazione [Dao.xml] per utilizzare altri SGBD (Firebird, MySQL, Postgres, SQL Server)

13.2.4. Generazione del e del DLL dal livello [dao]

Una volta scritta e testata la classe [PamDaoNHibernate], si genererà la classe DLL dal livello [dao] nel modo seguente:

  • [1], i programmi di test sono esclusi dall'assemblaggio del progetto
  • [2,3], configurazione del progetto
  • [4], generazione del progetto
  • il file DLL viene generato nella cartella [bin/Release] [5]. Lo aggiungiamo ai file DLL già presenti nella cartella [lib] [6]:

13.3. Il livello applicativo

Torniamo all’architettura generale dell’applicazione [SimuPaie]:

Consideriamo ora che il livello [dao] sia stato implementato e incapsulato in DLL [pam-dao-spring-nhibernate.dll]. Ci concentriamo ora sul livello [metier]. È questo che implementa le regole di business, in questo caso le regole per il calcolo dello stipendio.

Il progetto Visual Studio del livello di business potrebbe presentarsi come segue:

  • in [1] l’intero progetto configurato dai file [App.config] e [Dao.xml]. Il file [App.config] è identico a com'era 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 ID pammetier. La dichiarazione di quest’ultimo è 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 il DLL [pam-dao-spring-nhibernate] del livello [dao] esaminato in precedenza.

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

  • in modalità console tramite il 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 modificati.


Dopo i test, verrà generato il DLL dal livello [metier], che verrà denominato [pam-metier-dao-spring-nhibernate]:

  • in [1], il test NUnit è andato a buon fine
  • in [2], il DLL generato dal progetto

Aggiungeremo il file DLL del livello [metier] ai file DLL già presenti nella cartella [lib] [3]:

13.4. Il livello [web]

Torniamo all’architettura generale dell’applicazione [SimuPaie]:

Consideriamo che i livelli [dao] e [métier] siano già stati realizzati e incapsulati nei livelli DLL e [pam-dao-spring-nhibernate, pam-metier-dao-spring-nhibernate]. Passiamo ora alla descrizione del livello web.

Il progetto Visual Web Developer del livello [web] viene innanzitutto ottenuto 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 punti:

  • in [1], è configurato dai file [Dao.xml] e [Web.config]. [Dao.xml] non era presente in [pam-v7] e il file [Web.config] deve includere la configurazione Spring / NHibernate, mentre in [pam-v7], configurava solo NHibernate.
  • In [2], i file DLL dei livelli [dao] e [metier] sono quelli che abbiamo appena creato.

Il file [Dao.xml] è quello utilizzato nella creazione del livello [metier]. Il file [Web.config] è quello di [pam-v7] a cui si aggiunge la configurazione Spring / NHibernate che si trovava nei file [App.config] dei livelli [dao] e [metier]. Il file [Web.config] di [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>

  <!-- configurazione Spring -->
  <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]

Nelle righe 7-11 e 16-23 si ritrova la configurazione Spring presente nei file [App.config] dei livelli [dao] e [metier] creati in precedenza, con una sola 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] della cartella del progetto web. Con la sintassi


      <resource uri="Dao.xml" />

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


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

affinché il file [Dao.xml] venga cercato nella cartella [bin] della cartella del progetto web in esecuzione.


Domanda: implementare questa applicazione web su un computer.


13.5. Conclusion

Siamo passati dall'architettura:

all'architettura:

Si trattava di implementare il livello [dao] sfruttando le funzionalità offerte dall'integrazione di NHibernate da parte di Spring.

Abbiamo potuto constatare che ciò:

  • influiva sul livello [dao]. Quest’ultimo è stato più semplice da scrivere, ma ha richiesto una configurazione di Spring più complessa.
  • influiva in misura marginale sui livelli [metier] e [web]

Abbiamo avuto qui un altro esempio dell’utilità delle architetture a livelli.