Più volte mi è stata posta questa domanda che qui riassumo:
NHibernate nell'ambito di una session può caricare oggetti le cui collezioni vengono marcate come lazy. Gli oggetti delle collezioni non verranno caricati se non in seguito al richiamo della prima funzione che ne fa uso (Un count, il richiamo di un item e così via), il tutto mediante l'uso di un trasparente di un proxy.
Fin qui tutto bene.
I problemi iniziano quando un oggetto caricato (non completamente) in una session viene passato ad un altra session magari perchè appartenente ad una transazione applicativa (transazioni lunghe che hanno uno span su più transazioni di database), o magari all'interno di una applicazione ASP.NET dove viene passato tra una richiesta ed un'altra.
Nella seconda session, al tentativo di inizializzare le collezioni marcate come lazy si ottiene una LazyInitializationException.
Perchè? [Urla indistinte del programmatore di turno]
Non potrebbe NHibernate (in maniera nascosta all'utente) fare tutto il lavoro sporco e completarsi il lavoro di inizializzazione?
La risposta è:
Potrebbe ma introdurrebbe più problemi che soluzioni, poichè ci sarebbero gravi conseguenze sull'accesso transazionale.
Mi spiego:
Ammettiamo che nella prima sessione si utilizzi una transaction demarcation (si faccia partire una nuova transazione) che sta ovviamente su una propria connessione; se nella successiva session si dovesse inizializzare la parte lazy si dovrebbe far partire una nuova connessione, perdendo coerenza e fuoriuscendo dalla demarcazione della prima transazione.
Il fatto è che non bisogna considerare NHibernate (in particolare l'oggetto session) come un mero servizio di richiesta/persistenza di entità da un database, al di fuori da contesti di Unit of Work,
Nhibernate è un servizio di on line transaction processing e come tale deve offrire una propria demarcazione transazionale nelle operazioni di persistenza.
E allora, come risolviamo?:
La soluzione è ovviamente pensare ad un giusto disegno applicativo e ad una corretta demarcazione delle unit of work:
Ecco una semplicissima strategia:
..
obj = session1.Get(typeof(MiaClasse), id);
// lavoro su obj
session.Flush();
// Passo obj ad una nuova session
session2.Update(obj);
// oppure session2.Lock(obj, LockMode.Read);
// lavoro su obj dove eventualmente può essere inizializzata la parte lazy dell'oggetto
session2.Flush();
L'update sulla seconda session è solo schedulato e non eseguito grazie al Transparent Write Behind.