2010-10-29 6 views
5

Mein aktuelles Projekt verwendet NHibernate 3.0b1 und die API. Ich bin ziemlich flüssig in LINQ, aber ich habe absolut keine Erfahrung mit HQL oder der ICriteria API. Eine meiner Abfragen wird von der IQueryable-API nicht unterstützt. Daher nehme ich an, dass ich eine der vorherigen APIs verwenden muss - aber ich habe keine Ahnung, wo ich anfangen soll.Wie kann ich diese LINQ-Abfrage mithilfe der NHibernate ICriteria API ausdrücken?

Ich habe versucht, im Internet nach einem guten "Erste Schritte" Leitfaden für ICriteria zu suchen, aber die einzigen Beispiele, die ich gefunden habe, sind entweder viel zu einfach, um hier anzuwenden oder viel zu weit fortgeschritten für mich zu verstehen. Wenn jemand gute Lernmaterialien mitbringt, wäre es sehr zu schätzen.

Auf jeden Fall habe ich die Abfrage bin das Objektmodell wie dieses gegen aussieht (stark vereinfacht, nicht-relevanten Eigenschaften weggelassen):

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; } 
} 

A Ticket hat eine Sammlung von TicketAction seiner Geschichte beschreibt. TicketAction Untertypen gehören CreateAction, ReassignAction, CloseAction, etc. Alle Tickets haben eine CreateAction zu dieser Sammlung hinzugefügt, wenn erstellt.

Diese LINQ-Abfrage sucht nach Tickets, die von jemandem mit dem angegebenen Namen erstellt wurden.

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

Die OfType<T>() Methode verursacht eine NotSupportedException geworfen werden. Kann ich dies stattdessen mit ICriteria tun?

Antwort

2

versuchen Sie etwas wie das. Es ist nicht kompiliert, aber es sollte funktionieren, solange IEnumerable<TicketAction> Actions und Person TakenBy nie null ist. Wenn Sie es im Ticketkonstruktor auf eine leere Liste setzen, löst das ein Problem mit Nullen.

Wenn Sie einen Verweis auf das Ticket-Objekt in den TicketAction hinzufügen, könnten Sie so etwas tun:

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; 

Nach meiner Erfahrung nhibernate hat Mühe mit den Kriterien, wenn es um Listen kommt, wenn die Liste ist die Objektseite - so wie es in Ihrem Fall ist. Wenn es sich um eine Liste von Werten auf der Eingabeseite handelt, können Sie Expression.Eq verwenden. Ich musste immer Wege finden, um diese Einschränkung durch Linq zu finden, wo ich eine erste Ergebnismenge gefiltert bekomme, so gut ich kann, dann filtere ich wieder mit linq, um zu bekommen, was ich brauche.

+0

Ich würde lieber keinen Rückverweis auf ein hinzufügen müssen 'TicketAction's' Ticket' wenn ich helfen kann da das andere Probleme mit sich bringen wird, aber danke für den Tipp. :) –

+0

Die andere Art, wie wir Situationen wie diese behandelt haben, war, die HQL in einem String Builder zu erstellen. Wir haben versucht, Linq zu Nhibernate-Ausdrücken zu verwenden, aber auch diese unterstützen keine Sammlungen, wenn die Sammlung auf der Objektseite ist. Leider übersetzt die Methode "Contains" nicht in Linq in nHibernate. – Josh

+0

Ich ging schließlich mit Ihrem Vorschlag (Hinzufügen eines Verweises auf das Ticket auf TicketAction), aber ich am Ende mit einem Fehler: "NHibernate.QueryException: Eigenschaft konnte nicht aufgelöst werden: TakenBy.Name von: CreateAction " –

0

OfType wird unterstützt. Ich bin mir nicht sicher, ob ToUpper dies tut, aber wenn SQL ignoriert, spielt es keine Rolle (solange Sie die Abfrage nicht auch im Speicher ausführen ...). Hier ist ein funktionierendes Unit-Test aus dem nHibernate.LINQ Projekt:

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); 

Vielleicht ist Ihre Abfrage aussehen sollte wie folgt aus:

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