2010-10-29 12 views
5

Mi proyecto actual usa NHibernate 3.0b1 y la API NHibernate.Linq.Query<T>(). Soy bastante fluido en LINQ, pero no tengo absolutamente ninguna experiencia con HQL o la API de ICriteria. Una de mis consultas no es compatible con la API IQueryable, así que supongo que necesito usar una de las API anteriores, pero no tengo idea de dónde empezar.¿Cómo expreso esta consulta LINQ utilizando la API de NHibernate ICriteria?

He intentado buscar en la web una buena guía de "inicio" para ICriteria, pero los únicos ejemplos que he encontrado son demasiado simplistas para aplicar aquí o demasiado avanzados para que los entendamos. Si alguien tiene algunos buenos materiales de aprendizaje para transmitir, sería muy apreciado.

En cualquier caso, el modelo de objetos de consulta que estoy en contra es similar al siguiente (en gran medida simplificados, propiedades no relevantes omitido):

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 tiene una colección de TicketAction que describe su historia. TicketAction subtipos incluyen CreateAction, ReassignAction, CloseAction, etc. Todos los boletos tienen un CreateAction agregado a esta colección cuando se creó.

Esta consulta LINQ está buscando tickets creados por alguien con el nombre dado.

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

El método OfType<T>() provoca una NotSupportedException a ser lanzado. ¿Puedo hacer esto usando ICriteria en su lugar?

Respuesta

2

intente algo como esto. No está compilado, pero debería funcionar siempre que IEnumerable<TicketAction> Actions y Person TakenBy nunca sean nulos. Si lo configura en una lista vacía en el constructor del ticket, eso resolverá un problema con valores nulos.

Si se agrega una referencia al objeto de entradas en el TicketAction, se podría hacer algo como esto:

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; 

En mi experiencia, nhibernate tiene problemas con criterios cuando se trata de listas cuando la lista está en el lado del objeto, como es tu caso. Cuando se trata de una lista de valores en el lado de entrada, puede usar Expression.Eq. Siempre he tenido que encontrar la manera de superar esta limitación a través de linq, donde obtengo un conjunto de resultados inicial filtrado lo mejor que puedo, luego filtrar de nuevo con linq para obtener lo que necesito.

+0

prefiero no tener que añadir un respaldo referencia a una 'Ticket' de 'TicketAction' si puedo evitarlo, ya que eso introducirá otros problemas, pero gracias por la sugerencia. :) –

+0

La otra forma en que manejamos situaciones como esta fue crear realmente el HQL en un generador de cadenas. Intentamos usar Linq para expresiones Nhibernate, pero tampoco admiten colecciones cuando la colección está en el lado del objeto. Desafortunadamente, el método "Contiene" no se traduce en Linq para nHibernate. – Josh

+0

Fui con su sugerencia después de todo (agregando una referencia al ticket en TicketAction), pero termino con un error: "NHibernate.QueryException: could not resolve property: TakenBy.Nombre de: CreateAction " –

0

OfType es compatible. No estoy seguro de que ToUpper lo esté, pero como SQL ignora el caso, no importa (siempre y cuando no estés ejecutando la consulta en la memoria ...). Aquí está una prueba de unidad de trabajo del proyecto 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); 

Tal vez su consulta debe parecerse más a lo siguiente:

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