2010-06-28 23 views
5

estoy usando NHibernate y tengo las dos clases siguientes, los cuales se asignan mi esquema de base de datos:Cómo usar NHibernate para recuperar elementos con un criterio en una lista de

public class A 
{ 
    public virtual int Id { get; set;} 
    public virtual List<B> MyList { get; set; } 
} 

public class B 
{ 
    public virtual int Id { get; set; } 
    public virtual DateTime Date { get; set; } 
    public virtual A FKtoA { get; set; } 
} 

me gustaría conseguir todas las entradas de tabla A que tiene todos los elementos de su propiedad MyList con una fecha inferior a un valor dado.

¿Cómo puedo hacer eso con una elegante sintaxis NHibernate?

Respuesta

1

te debo la "elegante" parte ... :-)

Este es un posible HQL. Tenga en cuenta que invirtió su condición: en lugar de buscar "A que tiene todos los elementos de su propiedad MyList con una fecha inferior a un valor determinado", busco "A que no tenga ningún elemento de su MyList propiedad con una fecha mayor o igual que un valor determinado ".

from A a 
where a not in 
     (select a1 
     from A a1, B b 
     where b.Date >= :date 
     and b in elements(a1.MyList)) 

Uso:

var results = session.CreateQuery("hql from above") 
        .SetParameter("date", DateTime.Today) 
        .List(); 

Tenga en cuenta que, si se declara una relación bidireccional entre A y B (añadiendo un A propiedad), la consulta es mucho más simple:

from A a 
where a not in 
     (select b.A 
     from B b 
     where b.Date >= :date) 

Actualización: aquí es cómo hacerlo con Criteria:

session.CreateCriteria<A>().Add(
    Subqueries.PropertyNotIn("id", 
          DetachedCriteria.For<A>() 
           .CreateCriteria("MyList") 
           .SetProjection(Projections.Property("id")) 
           .Add(Restrictions.Ge("Date", DateTime.Today)))) 
+0

buen punto para la relación bidireccional: agregaré una propiedad de tipo A en mi clase B. HQL es genial, pero me preguntaba si no podríamos obtener algo con los métodos DetachedCriteria() y Projections.Max() – PierrOz

+0

OK, Agregué la opción Criteria (no sería difícil tomar eso y cambiarlo para usar 'Subqueries.PropertyIn' e invertir la subconsulta para usar una proyección). Sin embargo, Criteria es más útil para consultas dinámicamente construidas (búsqueda). Mire todo el ruido, comparado con el HQL. –

+1

maravilloso muchas gracias !! – PierrOz

0

Utilice esta

ICriteria criteria = session.CreateCriteria<ClassOfTableOne>(); 
criteria.CreateAlias("FieldNameOfTypeTable2","aliasName"); 
criteria.SetFetchMode("aliasName", FetchMode.Join); 
criteria.Add(Restrictions.Lt("aliasName.Date", yourdate)); 
+0

Esa consulta NO devuelve lo que PierrOz espera. –

0

si la clase B es como la siguiente (donde la propiedad Mi lista de A busca este FK)

public class B 
{ 
    public virtual int Id { get; set; } 
    public virtual DateTime Date { get; set; } 
    public virtual A FK_ToA { get; set; } 
} 

entonces creo que está buscando (HQL)

nhSes.CreateQuery("select b.FK_ToA from B b where b.Date < :passedDate") 
    .SetTimestamp("passedDate", DateTime.Now).List<A>() 
-1

La respuesta aceptada actualmente se basa en una subconsulta correlacionada, que como regla general es solo "mala SQL".

Es mucho mejor expresar esto utilizando la semántica basada en conjuntos en lugar de un enfoque más funcional.

Esencialmente, usted quiere que su SQL para tener este aspecto:

SELECT 
A.Id 
FROM A 
LEFT OUTER JOIN B ON A.Id = B.FKtoA 
WHERE B.Date < @MyDate 

Esto lee como "quiero un conjunto de columnas de A en lo relacionado con un conjunto de B donde B Fecha es menor que un cierto valor". Esto se puede lograr mediante la API ICriteria:

ICriteria criteria = session.CreateCriteria<A>(); 
criteria.CreateAlias("MyList", "b", JoinType.LeftOuterJoin) 
criteria.Add(Restrictions.Lt("b.Date", myDate)); 
criteria.SetResultTransformer(new DistinctRootEntityResultTransformer()); 
criteria.List<A>(); 

Parte del truco está utilizando de NHibernate incorporado en DistinctRootEntityResultTransformer: ya que la combinación externa izquierda podría volver varias instancias de A por B, queremos que nuestros ICriteria a devolver sólo el instancias distintas (suponiendo que no nos importa ordenar o lo que sea).

+0

Te sugiero que respaldes tus "reglas generales" con algunos datos concretos. Además, su SQL sugerido no funcionaría: el uso de B en la sentencia WHERE lo convierte en una unión interna (lo mismo ocurre con su Criteria, que tiene la carga adicional del transformador distintivo del lado del cliente) –

+0

NO provocaría una unión interna como lo he especificado como una combinación externa. Puede verificar esto a través de un generador de perfiles. Básicamente, hay dos enfoques para el problema: una sola consulta (que es la solución que he sugerido) o dos consultas (ya sea a través de una subconsulta correlacionada o dos consultas completamente separadas). Ambos tienen pros/contras. Mencionó la desventaja principal de una sola consulta. En el caso de la subconsulta correlacionada, mira esto: http://stackoverflow.com/questions/141278/subqueries-vs-joins –

+0

Un filtro fuera de la condición JOIN lo convierte en una unión interna (* pruébalo *) . Puede hacer que forme parte de la condición de unión con HQL (usando la cláusula WITH) pero no con Criteria. Además: la subconsulta en este caso NO está correlacionada (no estoy usando elementos de la consulta externa dentro de la interna), por lo que es equivalente a una combinación, al menos con motores de DB decentes. –

Cuestiones relacionadas