Gestione della persistenza con NHibernate

3 pagine in totale: <<Indietro 1 [2] 3 Avanti >>

Gli altri nodi del mapping indicano quali sono le proprietà della classe cliente; nel caso d'esempio, né il tipo e né la colonna sono specificati in maniera esplicita e pertanto saranno determinati automaticamente da NHibernate stesso, il primo via reflection e il secondo per omonimia con la proprietà stessa.

Il mapping della classe Fattura è estremamente simile a quello precedentemente descritto, o almeno lo è fintanto che non ci si imbatte nella proprietà Cliente. Questa infatti non rappresenta un dato primitivo, bensì una relazione molti-a-uno, corrispondente ad una foreign-key nel mondo relazionale. Pertanto in luogo del tag property è necessario usare il tag many-to-one.

<class name="Fattura" table="FATTURE">
  ...
  <many-to-one name="Cliente" column="ClienteId" />
  ...
</class>

La complessità non è aumentata poi di molto, dato che anche in questo caso l'O/RM è in grado di stabilire via reflection qual è il tipo coinvolto nell'associazione. Le cose diventano leggermente più difficili nel caso della collection Dettagli, tipico esempio di una relazione Master-Detail (one-to-many nel gergo di NH), il cui mapping è grossomodo uguale a quanto segue.

<bag name="Dettagli" cascade="all-delete-orphan" generic="true" lazy="true" inverse="true">
  <key column="FatturaId" />
  <one-to-many class="DettaglioFattura"/>
</bag>

La sintassi è divenuta un po' più prolissa, dato che abbiamo dovuto indicare la tipologia di collezione da utilizzare (bag) corrispondente in .NET al tipo della proprietà corrispondente IList<DettaglioFattura>. Gli attributi cascade, lazy e inverse necessitano qualche commento:

  • Cascade rappresenta un concetto chiave nel mondo di NHibernate e specifica il comportamento da tenere nei confronti degli elementi correlati ad un oggetto persistito. L'impostazione assegnata nell'esempio, all-delete-orphan, indica che la gestione della persistenza di una fattura deve essere automaticamente estesa anche ai suoi dettagli, effettuando un monitoraggio su quali vengono aggiunti, modificati o rimossi (divenendo pertanto "orfani") e producendo le opportune query di INSERT, UPDATE e DELETE in fase di memorizzazione.
  • Lazy serve ad attivare il Lazy Load, una funzionalità estremamente interessante che, nel caso in questione, evita di recuperare l'elenco dei dettagli contestualmente alla relativa fattura, rimandando il caricamento fino al momento in cui essi diventano effettivamente necessari.
  • Inverse è un concetto un po' più complesso da capire e, per farlo, è necessario tenere a mente che NHibernate effettua un monitoraggio continuo sulla composizione degli elementi di una collection, controllando quali vengono rimossi o aggiunti. La nostra relazione, come si vede anche dal diagramma in figura 2, è però bidirezionale: anche il dettaglio ha un riferimento ad una fattura ed entrambi i versi di questa associazione sono rappresentativi della medesima foreign key su database. Se entrambi i legami venissero presi in considerazione ai fini della persistenza di un nuovo dettaglio, sarebbero generate due query analoghe con una conseguente perdita di prestazioni. Per questa ragione è necessario che una delle due associazioni non produca alcun codice SQL, dal momento che la sua finalità è unicamente quella di garantire la navigabilità della relazione in entrambi i sensi. In sostanza, la proprietà Dettagli viene definita semplicemente come l'inversa della proprietà Fattura della classe Dettaglio.

La session è il vero e proprio cuore pulsante di NHibernate, l'oggetto a cui dobbiamo rivolgere lo sguardo ogni volta che si presenta la necessità di interagire con la base dati. La creazione di una session è un'operazione estremamente veloce e leggera per il sistema e può essere effettuata solo tramite l'oggetto SessionFactory. Al contrario, invece, ottenere un'istanza di SessionFactory è molto più complesso e oneroso per l'applicativo; per questa ragione solitamente si preferisce esporre una factory tramite una classe statica (SessionHelper nel nostro esempio), assicurando quindi che ne venga costruita una ed una sola per ciclo di vita dell'applicazione.

public class SessionHelper
{
  private static readonly Configuration cfg;
  private static readonly ISessionFactory sessionFactory;

  static SessionHelper()
  {
    cfg = new Configuration();
    cfg.AddAssembly(typeof(Cliente).Assembly);
    sessionFactory = cfg.BuildSessionFactory();
  }

  public static ISession GetSession()
  {
    return sessionFactory.OpenSession();
  }
}

Esempi pratici di persistenza con NHibernate

A questo punto tutti gli aspetti legati al mapping tra dominio e database sono a posto e creare un nuovo cliente su database (classe Cliente nel dominio) diventa una questione di poche righe di codice.

// creazione dell'istanza del nuovo cliente
Cliente cliente = new Cliente();
cliente.RagioneSociale = "Cliente di prova Spa";
cliente.DataContrattoFornitura = DateTime.Today;
cliente.Fido = 50000;

// Apertura di una session e memorizzazione su database
using (ISession session = SessionHelper.GetSession())
{
  session.SaveOrUpdate(cliente);
  session.Flush();

  // a questo punto il cliente è memorizzato.
}

È NHibernate a svolgere tutto il lavoro, preoccupandosi di:

  • capire se l'entità (in questo caso di tipo Cliente) è nuova o è già esistente in base all'identificativo;
  • creare ed eseguire l'opportuna query di INSERT o UPDATE;
  • eventualmente recuperare (come si può facilmente verificare con un profiler) l'identificativo generato automaticamente da SQL Server.

Anche persistere un grafo più complesso di entità, come una fattura e i relativi dettagli, è una operazione realizzabile con codice del tutto analogo, sfruttando il fatto che, grazie all'impostazione di cascade decisa sul mapping, l'operazione di salvataggio dell'entità padre coinvolge automaticamente anche tutti i figli. Dato che le query eseguite sono più di una, è bene che condividano lo stesso contesto transazionale.

// Creazione di una nuova istanza di Fattura e del relativo dettaglio
Fattura fattura = new Fattura();
fattura.Cliente = cliente;
Dettaglio d = new Dettaglio();
d.Fattura = fattura;
// ... altro codice ...

// Apertura di una session e memorizzazione su database
using (ISession session = SessionHelper.GetSession())
{
  // mi aspetto diverse INSERT, quindi
  // questa volta usiamo le transazioni!!
  ITransaction tx = session.BeginTransaction();
  try
  {
    session.SaveOrUpdate(fattura);
    tx.Commit();
  }
  catch(Exception ex)
  {
    tx.Rollback();
  }
}

Un discorso analogo vale per la cancellazione, tramite il metodo Session.Delete, operazione che NHibernate gestisce con un'occhio all'integrità referenziale, eliminando preventivamente il dettaglio per poi procedere all'eliminazione della fattura.

3 pagine in totale: <<Indietro 1 [2] 3 Avanti >>

Attenzione: Questo articolo contiene un allegato

Contenuti dell'articolo

Commenti
Dai un voto a questo articolo, ci aiuterà a migliorare il nostro sito (1 è il voto minimo, 5 il massimo).

Per procedere al rating dell'articolo devi essere autenticato.

TUTORIALS
TOP TEN ARTICOLI
NOTIFICHE

Iscriviti alla nostra newsletter nuoviarticoli per ricevere e-mail le notifiche!

Indirizzo e-mail:
PROVIDER ASP.NET 2.0

Seleziona il database per avere il web.config pronto per Membership, Roles e Profile API.



IN EVIDENZA
MISC