Blog Stats
  • Posts - 3
  • Articles - 3
  • Comments - 0
  • Trackbacks - 0

 

Introduzione a Fluent NHibernate

Fluent NHibernate è un tool open source che consente di mappare e auto mappare una Data Base sul modello dati di un applicazione.
NHibernate è un ORM che consente di mappare database relazionali dei più diffusi vendor, i quali sono:
• Microsoft SQL Server 2005/2008
• Oracle
• Microsoft Access
• Firebird
• PostgreSQL
• DB2 UDB
• MySQL
• SQLite

Questo lo rende un alternativa a Entity Framework qualora ci si trovi ad utilizzare un DBMS non supportato o un DBMS per il quale il costo della licenza sia esoso.



L’oggetto Session

L’oggetto Session si occupa di stabilire la connessione al motore della Base di Dati, di estrarre e di salvare i dati, che vengono effettivamente estratti dalla Base di Dati al primo acesso che si fa alle apposite strutture in menoria.
Di default l’accesso ai dati e di tipo “lazy”, cioè pigro. Essendo pigro, cioè facendo le cose solo in dopo, ovvero quando si hanno più in formazioni per capire cosa fare, si ottimizza l’accesso al database.

Un oggetto del Modello dei Dati si dice persistente se è caricato in una Sessione. La sessione mantiene una copia dello stato originale di un oggetto quando viene modificato in modo di poter capire il tipo di operazione da compiere e di conseguenza la query da generare quando si chiama un metodo di scrittura sulla Base di Dati. Tale insieme di dati si chiama Persistence Context.
Un oggetto resta persistente finchè è contetuto nella sessione. Quando viene scaricato dalla sessione divanta Distaccato (detatched). Quando viene creato ma non è ancora caricato nella sessione si dice che è Transiente.

L’oggetto sessione viene inizializzato tramite una configurazione che può trovarsi su file xml e su codice.
Nel caso in cui si scelga l’uso della configurazione su codice può essere scritta nel modo seguente:

public partial class SessionManager
    {
        public static ISessionFactory CreateFluentSessionFactory()
        {
            return Fluently.Configure()
                .Database(
                    MsSqlConfiguration.MsSql2008.ConnectionString(
                    ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString)).
                       Mappings(m =&gt; m.FluentMappings.AddFromAssemblyOf<sessionmanager>()).
                           BuildSessionFactory();
        }


MsSqlConfiguration.MsSql2008 indica quale libreria deve essere caricata.
Tale metodo ritorna un oggetto ISessionFactory da cui può essere poi istanziata una sessione per le operazioni sui dati.
Per iniziare una transazione di dati occorre innanzitutto aprire la sessione e ricevere l’oggetto di tipo ISession che rappresenta la sessione.

protected ISession GetSession()
        {
            return SessionManager
                .CreateFluentSessionFactory()
                    .OpenSession();
        }


Se ad esempio si vogliono salvare dei dati facendo uno di una transazione, si usa il metodo SaveOrUpdate

public void Save(object o)
        {
            ISession session = this.GetSession();
            using (ITransaction tx = session.BeginTransaction())
            {
                session.SaveOrUpdate(o);
                tx.Commit();
            }
        }


Il metodo SaveOrUpdate determina se si tratta di un nuovo item o dell’aggiornamento di dati già esistenti.
Se invece si volgiono estrarre dei dati in cui un dato campo ha un dato valore, si usa il metodo CreateCriteria che ritorna un oggetto di tipo Criteria; L'interfaccia net.sf.hibernate.Criteria rappresenta un'interrogazione nei confronti di una particolare classe persistente. La Session è un produttore di istanze di Criteria. Tramite il Criteria è possibile poi dichiarare le condizioni tramite il metodo Add che prende come argomento oggetti di tipo Expression oppure Restriction. Nel caso in esempio Restrictions.Eq(Proprietà, Valore della proprietà)

protected T Get<t>(string propertyName, object propertyValue)
        {
            ISession session = this.GetSession();
            return session.CreateCriteria(typeof(T))
                .Add(Restrictions.Eq(propertyName,
                      propertyValue).IgnoreCase()).UniqueResult<t>();
        }


I prototipi come quelli elencati possono essere poi usati dal livello de servizio per accedere ai dati.



Fluent NHibernate

Fluent NHibernate è una libreria che ha come principali caratteristiche quella di consentire il mapping dei dati da codice anziché da XML, l’uso di convenzioni per la mappatura, il che consente di scrivere meno codice.
Le mappature sono rappresentate da classi che ereditano da ClassMap<classe del modello> in cui classe del Modello è la classe da mappare.
Ci proponiamo in questo articolo di mappare il database descritto nello schema sottostante.


Descrive dei pacchetti turistici in cui ogni pacchetto ha una serie di destinazioni e ogni luogo può definire più destinazioni.
La classi del modello utilizzano la stessa nomenclatura utilizzata dallo schema dati. Vedremo dopo come gestire eventuali eccezioni a questa convenzione.

    public class gab_Destinazioni
    {
        public virtual int id { get; set; }
        public virtual string titolo { get; set; }
        public virtual string descrizione { get; set; }
        public virtual int PageId { get; set; }
        public virtual gab_Luoghi Luogo { get; set; }
        public virtual IList<gab_pacchetti> Pacchetti { get; set; }
 
        public gab_Destinazioni()
        {
            Luogo=new gab_Luoghi();
            Pacchetti = new List<gab_pacchetti>();
        }
    }


public class gab_Pacchetti
    {
        public virtual int id { get; set; }
        public virtual string nome { get; set; }
        public virtual string descrizione { get; set; }
        public virtual int durata { get; set; }
        public virtual int categoria { get; set; }
        public virtual DateTime validoDa { get; set; }
        public virtual DateTime validoA { get; set; }
        public virtual int PageId { get; set; }
 
        public virtual IList<gab_destinazioni> Destinazioni { get; set; }
        public virtual bool IsValid
        {
            get { return true; }
        }
 
        public gab_Pacchetti()
        {
            Destinazioni = new List<gab_destinazioni>();
        }
    }


L’associazione molti a molti tra Destinazioni e Pacchetti è rappresentata dalle rispettive liste di oggetti del tipo associato.
La mappatura di conseguenza sarà la seguente:

    public class PacchettiMapping : ClassMap<gab_pacchetti>
    {
        public PacchettiMapping()
        {
            Id(x =&gt; x.id);
            Map(x =&gt; x.categoria);
            Map(x =&gt; x.descrizione);
            Map(x =&gt; x.durata);
            Map(x =&gt; x.nome);
            Map(x =&gt; x.validoA);
            Map(x =&gt; x.validoDa);
            Map(x =&gt; x.PageId);
            HasManyToMany(x =&gt; x.Destinazioni).Cascade.All()
                .Table("gab_Pacchetti_Destinazioni");
        }
    }


Id definisce la chiave primaria, tramite l’espressione che associa all’istanza di gab_Pacchetto alla proprietà che è tale identificativo univoco.
Il metodo Map mappa i campi.
HasManyToMany definisce l’associazione molti a molti tra le due entità. Cascade significa che l’azione invocata dal mapping deve propaagarsi alle entità dipendenti. All che si propaga a tutte.
In questo caso viene aggiunto alla mappatura molti a molti il nome della tabella che definisce l’associazione.

Le destinazioni hanno una mappatura simile:

public class DestinazioniMapping : ClassMap<gab_destinazioni>
    {
        public DestinazioniMapping()
        {
            Id(x =&gt; x.id);
            References(x =&gt; x.Luogo).Column("IdLuogo").Cascade.All();
            Map(x =&gt; x.PageId);
            Map(x =&gt; x.descrizione);
            Map(x =&gt; x.titolo);
            HasManyToMany(x =&gt; x.Pacchetti).Cascade.All().Inverse()
                .Table("gab_Pacchetti_Destinazioni");
        }
    }


Utilizziamo References per definire l’associazione molti a uno di gab_luogo. Si specifica il nome della colonna poiche per convenzione tale nome dovrebbe essere “luogo_id” ma non avendo rispettato la convenzione di Fluent occorre dichiarare quale è il nome della colonna. In questo caso la dichiarazione dell’associazione molti a molti chiama anche il metodo Inverse il che significa che l’altro estremo della relazione è responsabile del salvataggio.

public class LuoghiMapping : ClassMap<gab_luoghi>
    {
        public LuoghiMapping()
        {
            Id(x =&gt; x.id);
            Map(x =&gt; x.Descrizione);
            Map(x =&gt; x.Nazione);
            Map(x =&gt; x.Nome);
            Map(x =&gt; x.Provincia);
            Map(x =&gt; x.Zona);
            Map(x =&gt; x.page_id);
 
            HasMany(x =&gt; x.Destinazioni).Cascade.All().Inverse();
        }
    }


La mappatura del luogo invece ha il costrutto HasMany poiché un luogo può essere meta di più destinazioni. Anche in questo caso c’è Inverse(), altrimenti viene salvata la destinazione prima del luogo e questo causa un’eccezione.


Comments have been closed on this topic.
 

 

Copyright © Federico Pranio