Il pattern LazyLoad è quasi sempre utilizzato in 
NHibernate per il caricamento delle collection di childs in una relazione 
Master/Detail. Non tutti sanno invece che è possibile applicare questo pattern a 
tutti i tipi di relazioni, potendo così ottimizzare di molto la dimensione delle 
query che vengono effettuate su DB e la mole di dati che viene recuperata.
    Capita spesso di avere classi con parecchie 
relazioni Many-To-One, vuoi perché si tratta di classi "dettaglio" 
        di qualcosa, vuoi perché se 
creo la classe Articolo magari questa espone una proprietà Tipo di tipo 
(scusate il gioco di parole) TipoArticolo, ecc.ecc... Se non si utilizza il 
LazyLoad anche per questo tipo di relazioni, NHibernate effettua query con un gran numero 
di Join che, come sappiamo, possono minare le performance delle 
applicazioni.
Ho scritto una piccola applicazione di esempio, 
scaricabile a questo link          , 
che persiste sul DB un Articolo e un TipoArticolo e poi ne forza una lettura. 
Analizziamo il mapping di Articolo:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" default-lazy="false" 
    default-access="field.camelcase" >
    <class name="NHLazyTest.Articolo, NHLazyTest" table="Articoli">
        
        <!-- .... un po' di cose qui .... -->
        
        <many-to-one name="Tipo" class="NHLazyTest.TipoArticolo, NHLazyTest" />
    </class>
</hibernate-mapping>
Come si vede, c'è un riferimento many-to-one 
alla classe TipoArticolo, che a sua volta ha il seguente mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" 
    default-lazy="false" default-access="field.camelcase" >
    <class name="NHLazyTest.TipoArticolo, NHLazyTest" table="TipiArticolo" >
        <!-- .... un po' di cose qui .... -->
    </class>
</hibernate-mapping>
Non è che ci sia molto da commentare, se non quel 
default-lazy=false per modificare il comportamento 
standard di NHibernate 1.2 che tende a caricare tutte le entity in LazyLoad e 
che, nel caso si utilizzi una versione precedente, è perfettamente inutile! 
Se eseguiamo una lettura di un articolo dal 
DataBase, tramite il seguente snippet di codice
using (ISession session = SessionHelper.GetSession())
{
    articolo = session.Get<Articolo>(id);
}
 il risultato sarà che NHibernate proverà 
a recuperare tutti i dati da DB con una sola query, che sarà simile alla 
seguente:
exec sp_executesql N'SELECT articolo0_.ID_Articolo as ID1_1_, articolo0_.Tipo as Tipo1_1_, 
articolo0_.Descrizione as Descrizi2_1_1_, tipoartico1_.ID_TipoArticolo as ID1_0_, 
tipoartico1_.Descrizione as Descrizi2_0_0_ FROM Articoli articolo0_ 
left outer join TipiArticolo tipoartico1_ on articolo0_.Tipo=tipoartico1_.ID_TipoArticolo WHERE 
articolo0_.ID_Articolo=@p0',N'@p0 uniqueidentifier',@p0='B3D1E0B6-295D-452E-A834-4598052EA9C6'
 
Tutto ciò va benone nel caso in cui le 
entity coinvolte siano poche, ma se Articolo avesse altre relazioni il calo di 
performance potrebbe essere pesante, intanto perché parecchi join non fanno bene 
alla velocità e poi perché magari stiamo recuperando delle informazioni che in 
un certo contesto non ci interessano affatto.
Cosa fare allora? Per prima cosa modifichiamo 
leggermente il mapping di TipoArticolo per dire a NHibernate che tutte le 
istanze di TipoArticolo dovranno supportare il LazyLoad:
<class name="NHLazyTest.TipoArticolo, NHLazyTest" 
    table="TipiArticolo" lazy="true">
Ora 
chiediamoci: come viene materialmente realizzato un LazyLoad? Beh, si crea un 
proxy della classe che, al primo accesso ad uno qualsiasi dei suoi membri, 
effettua il vero e proprio caricamento. Affinché però tutto funzioni, è 
necessario
  - che il tipo in oggetto abbia proprietà e metodi 
  dichiarati virtual o, in alternativa,
  
 - utilizzare un'interfaccia in luogo di un tipo specifico, alla stessa stregua 
  di come accade per le collection.
 
Nel primo caso non dobbiamo far altro che adattare 
un po' il codice del domain, pena un'eccezione in fase di creazione della 
SessionFactory, nel secondo possiamo utilizzare l'attributo proxy per 
specificare l'interfaccia da utilizzare. In entrambi i casi, comunque, una volta 
ricompilato il programma, eseguendo lo stesso snippet di prima, NHibernate 
esegue sul DB una select parecchio più semplice che, come si nota, coinvolge la 
sola tabella Articoli:
exec sp_executesql N'SELECT articolo0_.ID_Articolo as ID1_0_, articolo0_.Tipo as Tipo1_0_,
articolo0_.Descrizione as Descrizi2_1_0_ FROM Articoli articolo0_ WHERE 
articolo0_.ID_Articolo=@p0',N'@p0 uniqueidentifier',@p0='A460678E-1611-4394-A7BA-236F6F68DBBA'
Indagando meglio, si può notare che il TipoArticolo 
ritornato è un'istanza di una classe il cui nome è all'incircaCProxyTypeTipoArticoloNHLazyTest_INHibernateProxy1 
e che, all'invocazione di uno qualsiasi dei suoi membri, carica il vero e proprio 
TipoArticolo, purché l'oggetto sia persistente, cioé collegato ad una 
Session.
  Due ultime considerazioni: innanzi 
tutto va da sé che tutto ciò è perfettamente integrato 
con il sistema di caching di NHibernate, il che vuol 
dire che il seguente snippet di codice
using (ISession session = SessionHelper.GetSession())
{
    // carico i due articoli dello stesso tipo
    articolo = session.Get<Articolo>(id);
    articolo1 = session.Get<Articolo>(id1);
    // questa forza l'eventuale lazy load del TipoArticolo corrispondente
    string s = articolo.Tipo.Descrizione;
    // questo non dovrebbe eseguire un'altra query perché TipoArticolo 
    // è già stato caricato dalla session (cache di primo livello)
    string s1 = articolo1.Tipo.Descrizione;
}
per recuperare l'istanza di TipoArticolo 
esegue una sola query sul DB, perché, come scritto nei 
commenti, al fetch su articolo1 l'engine si 
accorge che il medesimo TipoArticolo è già presente nella cache di primo 
livello, avendolo caricato in seguito all'esecuzione dello statement
string s = articolo.Tipo.Descrizione;
Inoltre se avessimo voluto comunque forzare il 
caricamento diretto di TipoArticolo, a prescindere dalla sua dichiarazione di 
fetching Lazy, sarebbe stato sufficiente modificare il mapping del many-to-one 
su Articolo in questo modo
<many-to-one name="Tipo" 
    class="NHLazyTest.TipoArticolo, NHLazyTest" 
    fetch="join" />
powered by IMHO 1.3