2010-10-29 15 views
5

Il mio progetto attuale utilizza NHibernate 3.0b1 e l'API NHibernate.Linq.Query<T>(). Sono abbastanza fluente in LINQ, ma non ho assolutamente alcuna esperienza con HQL o l'API ICriteria. Una delle mie query non è supportata dall'API IQueryable, quindi presumo di dover utilizzare una delle API precedenti, ma non ho idea da dove iniziare.Come si esprime questa query LINQ utilizzando l'API ICiteria NHibernate?

Ho provato a cercare sul Web una buona guida "iniziale" per ICriteria, ma gli unici esempi che ho trovato sono troppo semplicistici da applicare qui o troppo avanzati per me da capire. Se qualcuno ha dei buoni materiali didattici da passare, sarebbe molto apprezzato.

In ogni caso, il modello a oggetti che sto interrogando contro si presenta così (notevolmente semplificate, le proprietà non rilevanti omesso):

class Ticket { 
    IEnumerable<TicketAction> Actions { get; set; } 
} 
abstract class TicketAction { 
    Person TakenBy { get; set; } 
    DateTime Timestamp { get; set; } 
} 
class CreateAction : TicketAction {} 
class Person { 
    string Name { get; set; } 
} 

Un Ticket ha una collezione di TicketAction descrivere la sua storia. I sottotipi TicketAction includono CreateAction, ReassignAction, CloseAction, ecc. Tutti i biglietti hanno un CreateAction aggiunto a questa raccolta una volta creati.

Questa query LINQ cerca i ticket creati da qualcuno con il nome specificato.

var createdByName = "john".ToUpper(); 
var tickets = _session.Query<Ticket>() 
    .Where(t => t.Actions 
     .OfType<CreateAction>() 
     .Any(a => a.TakenBy.Name.ToUpper().Contains(createdByName)); 

Il metodo OfType<T>() provoca un NotSupportedException per essere gettato. Posso farlo usando ICriteria?

risposta

2

provare qualcosa come questo. Non è compilato, ma dovrebbe funzionare fino a IEnumerable<TicketAction> Actions e Person TakenBy non è mai nullo. Se lo si imposta su una lista vuota nel costruttore del ticket, questo risolverà un problema con null.

Se si aggiunge un riferimento all'oggetto Ticket nel TicketAction, si potrebbe fare qualcosa di simile:

ICriteria criteria = _session.CreateCriteria(typeof(CreateAction)) 
    .Add(Expression.Eq("TakenBy.Name", createdByName)); 

var actions = criteria.List<CreateAction>(); 

var results = from a in criteria.List<>() 
    select a.Ticket; 

Nella mia esperienza, NHibernate ha problemi con criteri quando si tratta di elenchi quando la lista è su il lato dell'oggetto - come è il tuo caso. Quando si tratta di un elenco di valori sul lato di input, è possibile utilizzare Expression.Eq. Ho sempre dovuto trovare il modo di aggirare questa limitazione attraverso linq, dove ottengo un set di risultati iniziale filtrato al meglio che posso, quindi filtro di nuovo con linq per ottenere ciò di cui ho bisogno.

+0

preferirei non dover aggiungere un back-riferimento ad un 'TicketAzione 'di TicketAction' se posso aiutarlo dato che questo introdurrà altri problemi, ma grazie per il suggerimento. :) –

+0

L'altro modo in cui abbiamo gestito situazioni come questa era di creare effettivamente l'HQL in un generatore di stringhe. Abbiamo provato a utilizzare le espressioni da Linq a Nhibernate, ma anche quelle non supportano le raccolte quando la raccolta si trova sul lato dell'oggetto. Sfortunatamente, il metodo "Contiene" non traduce in Linq su nHibernate. – Josh

+0

Ho seguito il tuo suggerimento dopo tutto (aggiungendo un riferimento al Ticket su TicketAction), ma alla fine ho riscontrato un errore: "NHibernate.QueryException: impossibile risolvere la proprietà: TakenBy.Nome di: CreateAction " –

0

OfType è supportato. Non sono sicuro che ToUpper lo sia, ma siccome SQL ignora il caso non ha importanza (a patto che tu non stia eseguendo la query anche in memoria ...). Ecco un test di unità di lavoro dal progetto nHibernate.LINQ:

var animals = (from animal in session.Linq<Animal>() 
       where animal.Children.OfType<Mammal>().Any(m => m.Pregnant) 
       select animal).ToArray(); 
Assert.AreEqual("789", animals.Single().SerialNumber); 

Forse la vostra query dovrebbe guardare più simile al seguente:

var animals = (from ticket in session.Linq<Ticket>() 
       where ticket.Actions.OfType<CreateAction>().Any(m => m.TakenBy.Name.Contains("john")) 
       select ticket).ToArray(); 
Problemi correlati