Ultimamente sono impazzito un po' cercando il metodo giusto che facesse la select giusta.
Mi sono incartato fra metodi ed interfacce... Find, Enumerable, Filter, CreateFilter, IQuery... ce ne sono troppi!
Così ho cercato di fare ordine, mi sono armato di pazienza, Google e del caro e lento manuale (fa sempre comodo, ma volete mettere la velocità di Google con la ricerca nel pdf? ;) ) ed ecco un po' di dritte che, spero, tornino utili.
Al limite serviranno a me a futura memoria :P
Non riporto tanti esempi, causa spazio e tempo, però il manuale (9.3. Querying) e la rete ne sono pieni.
Quello che intendo fare in questa sede è solo una sorta di riepilogo per meglio individuare come e quando usare i diversi metodi.

Per tutte le modalità si parte dall'oggetto Session.
Ipotizziamo di avere una tabella Users (usr_id int PK, usr_name varchar(20), usr_sex char(1)), mappata con la classe User seguente:

public class User{

private Int32 _id;

private string _name;

private char _sex;

public Int32 Id { get { return this._id; } set { this._id=value; } }

public string Name { get { return this._name; } set { this._name = value; } }

public char Sex { get { return this._sex; } set { this._sex = value; } }

}

objSession.Get() e Session.Get<>() servono a recuperare le istanze di oggetti di un determinato tipo e identificativo.
Il primo accetta come parametro un tipo, per l'appunto, e un id di tipo object (che dovrebbe essere l'identificativo dell'oggetto nel modello relazionale).
Il secondo metodo fa a meno del parametro di tipo perché è un metodo generico. Quest'ultimo, a differenza del metodo non generico, restituisce una istanza tipizzata dell'oggetto richiesto (il primo restituisce un object).
Per entrambi è possibile specificare una modalità di lock.

objSession.Load() e Session.Load<>() servono a recuperare le istanze di oggetti di un determinato tipo e identificativo.
Esiste un overload che consente di caricare un oggetto partendo da una istanza anziché fornirne il tipo.
La differenza con Get(): nel caso non venisse trovata una corrispondenza con l'identificativo fornito, verrebbe sollevata una eccezione; Get(), invece, ritornerebbe null.

objSession.Find()
Il metodo più semplice per fare le query: si specifica dove (from entity) e come (where).
Accetta, oltre alla query, un elenco (al limite 1 solo) di parametri ed un elenco (al limite uno solo) di dipi (di parametro, appunto).
I parametri (IType, enum NHibernateUtil) vengono rimappati su paramtri di tipo ADO.NET IDbCommand.
Il metodo è deprecato (in favore del metodo CreateQuery(), che offre maggiore supporto nel settaggio parametri).

Esempi:
IList users = objSession.Find("from User u where u.Name=?", name, NHibernateUtil.String);

IList users = objSession.Find("From User u where u.Name = ? or b.Id=?", new object[] {name, id}, new NHibernate.Type.IType[] {NHibernate.NHibernateUtil.String, NHibernate.NHibernateUtil.Int16});


objSession.Enumerable()
Nel caso si avesse bisogno di estrarre un elevato numero di risultati e non volessimo visualizzarli tutti, Enumerable!
Il metodo ritorna un tipo System.Collections.IEnumerable. Sarà compito dell'iterator caricare gli oggetti corrispondenti a richiesta in base all'identificatore della query iniziale (in pratica verrebbero eseguite n+1 query, dove n è il nr totale di risultati).
E' utilissimo (e performante) nel caso ci si aspetti che alcuni oggetti siano già caricati e/o presenti in cache (quella di NH) o si ripetano (pensate a join particolari).
Dal manuale: When no data is cached or repeated, Find() is almost always faster.
Ha le stesse signature di Find().
Se si intende fare una select di specifici campi, basta specificarli nella query.
Non l'ho ancora usato se non per fare delle prova e vedere che funziona ;) ...

Probabilmente avrebbe risolto anche il problema di Gianluca Gravina.

Sia con Find(), sia con Enumerable() è possibile fare raggruppamenti/aggregazioni, ottenendo quindi dei risultati che NH definisce "scalar results", ma che devo gestire facendo gli opportuni cast.
Esempi:
"select cat.Color, min(cat.Birthdate), count(cat) from Cat cat group by cat.Color"

IEnumerable results = sess.Enumerable("select cat.Type, cat.Name from DomesticCat cat");

IList results = sess.Find("select cat, cat.Mate.Name from DomesticCat cat");


foreach (object[] row in results)

{

Color type = (Color) row[0];

DateTime oldest = (DateTime) row[1];

int count = (int) row[2];

.....

}

objSession.Filter()
Consente di applicare ad una collection il filtro specificato. Ritorna una collection (filtrata, appunto).
Accetta 3 overload: collezione+query, collezione+query+parametro, collezione+query+parametri.

IQuery
Interfaccia utilissima nel caso si intenda (o sia necessario) gestire le paginazioni, estrarre solo un certo numero di righe o gestire un range.
Riusciamo ad ottenere una istanza di questa interfaccia mediante i seguenti metodi: CreateFilter, CreateQuery, GetNamedQuery. Vediamoli.

objSession.CreateFilter()
Consente di applicare ad una collection il filtro specificato (simile al Filter(), tranne che per il tipo di ritorno):
IList men = objSession.CreateFilter(users,"this.Sex='M'");

Riporto alcuni esempi tratti dal manuale:

The CreateFilter() method is also used to efficiently retrieve subsets of a collection without needing to initialize the whole collection:
objSession.CreateFilter( lazyCollection, "").SetFirstResult(0).SetMaxResults(10).List();

You can use a collection filter to get the size of a collection without initializing it:
(int) objSession.CreateFilter( collection, "select count(*)" ).List()[0]

IQuery q; // <- CreateQuery, CreateFilter, ecc...

q.SetFirstResult(20);

q.SetMaxResults(10);

IList users = q.List();

L'interfaccia IQuery permette di estrarre una IList (o IList<>), Enumerable (o Enumerable<>), utilizare parametri (NamedParameters), i parametri di uscita (ReturnTypes).
Per i parametri potrebbe essee utile ricorrere alla collezione NamedParameters o impostarli mediante i vari metodi q.SetTIPO (dove TIPO è Int16, Int32, String...).
Degno di nota il metodo UniqueResult() (e la versione tipizzata UniqueResult<>()) che restituisce il primo risultato o null.

objSession.CreateQuery()
Nulla di particolare: accetta come parametro una query HQL. Ritorna una istanza di IQuery.

objSession.GetNamedQuery()
Serve a recuperare una query configurata e salvata nel file di mapping. Ritorna la solita istanza IQuery.


objSession.CreateMultiQuery()
Ritorna una istanza di IMultiQuery. Usato per preparare un statement che è la sequenza di più statement SQL. Poi si esegue il tutto mediante il metodo List().
Le varie query vengono "aggiunte" mediante il metodo Add() (2 overload: uno accetta una stringa HQL, l'altro una istanza IQuery).

E fin qui abbiamo utilizzato HQL
Se vogliamo maggiore flessibilità (come se non bastasse... :) ), si può ricorrere all'interfaccia ICriteria.
Dal manuale: HQL is extremely powerful but some people prefer to build queries dynamically, using an object oriented API, rather than embedding strings in their .NET code. For these people, NHibernate provides an intuitive ICriteria query API.

Credo basta farsi il proprio linguaggio di query semplicemente estendendo (leggi implementando) l'interfaccia ICriterion.
A tal proposito ha scritto un bel post Gian Maria Ricci.
Mi riprometto di riprendere ICriteria e ICriterion quanto prima... per adesso ho scritto abbastanza.

Ma SQL?
E' possibile usare anche (e solo) quello: CreateSQLQuery().
Unico accorgimento: mettere gli alias fra graffe ({}).
Si possono avere parametri posizionali e nominativi, definire diverse tabelle per le query, con diversi alias, diversi tipi di ritorno... I JOIN!!!
Devo approfondirlo, ma sembra davvero molto potente.

Link di interesse:


Technorati :