Supponiamo di avere una entità del dominio che rappresenta un albero, albero mappato e persistito con NHibernate,  e abbiamo bisogno di caricare tutto l’albero in un singolo round-trip con il db.
image[1]
La soluzione proposta da Ayende è indubbiamente ottima e funzionale, e non è obbligatorio usare hql funziona con qualsiasi sistema di query venga usato.
var tree = provider.CreateCriteria<Branch>()
.List<Branch>()
.Where( z => z.Parent == null )
.ToList();
Quello che succede è decisamente figoso, in sostanza quello che fate è limitarvi a caricare tutti i dati della tabella e lasciare che sia l’Identity Map della ISession a fare il resto del giochetto per voi; quello che non viene tenuto conto nella soluzione proposta, ma nessuno l’ha chiesto del resto, è la possibilità che in fase di query venga imposto un filtro: voglio l’albero con i soli nodi il cui nome inizia per “P”:
var tree = provider.CreateCriteria<Branch>()
.Add( Restrictions.Like( "Name", "P%", MatchMode.Anywhere ) )
.List<Branch>()
.Where( z => z.Parent == null )
.ToList();
Implementando la “Restriction” succede che il primo livello viene correttamente filtrato ma per i figli NHibernate emette degli statement sql che tornano sul db nonostante al primo giro i dati vengano filtrati correttamente, ragionandoci su ci rendiamo conto che alla fine ha ragione lui e il “problema” è abbastanza semplice da aggirare:
var tree = provider.CreateCriteria<Branch>()
.SetResultTransformer( Transformers.DistinctRootEntity )
.CreateAlias( "ChildBranches", "cb", JoinType.LeftOuterJoin )
.Add( Restrictions.Like( "Name", "P%", MatchMode.Anywhere ) )
.Add( Restrictions.Like( "cb.Name", "P%", MatchMode.Anywhere ) )
.List<Branch>()
.Where( z => z.Parent == null )
.ToList();
Basta cioè propagare la condizione anche ai figli, a questo punto scopriamo che la ISession di NHibernate è veramente smart ed esegue correttamente il caricamento dell’albero facendo sempre e solo un round-trip verso il db.
.m