NHibernate Multiquery para carga ansiosa sin uniones
multi-query (2)
¿Es posible usar un multiquery y tener dos consultas hql devolviendo dos conjuntos diferentes de entidades donde uno de los conjuntos se utiliza en el otro y que la sesión "corrige" esto a través del primer nivel de caché?
Ej. Escenario (un tonto y podría resolverse con combinaciones)
public class Room
{
...
public virtual ISet<Bookings> Bookings {get;set;}
public virtual bool IsAvailible {get;set;}
...
}
public class Booking
{
...
}
Después de ejecutar un multicriterio con dos hql''s:
- devolviendo todas las habitaciones donde IsAvailible = true
- devolviendo todas las reservas que tienen una habitación que tiene una habitación que IsAvailible
al acceder a una sala desde el resultado y sus reservas, quiero que se resuelvan desde el segundo conjunto de resultados a través del primer nivel de la memoria caché de la sesión y evitando n + 1.
En general, NHibernate puede usar el caché para "combinar" los resultados de las consultas ejecutadas a través de Multiquery. Sin embargo, debe tenerse en cuenta que esto generalmente solo se aplica a los casos en que las colecciones perezosas se cargan sin restricciones de ningún tipo.
Ejemplos:
Invoice iAlias = null;
InvoiceDetails idAlias = null;
// Base-Query: get Invoices with certain condition
var invoices = session.QueryOver<Invoice>()
.Where(i => i.Number == "001")
.Future<Invoice>();
// Option 1: this will still cause N+1 if we iterate through invoices,
// because it doesn''t know better
var invoicedetails = session.QueryOver<InvoiceDetails>()
.JoinAlias(a => a.Invoice, () => iAlias)
.Where(() => iAlias.Number == "001")
.Future<InvoiceDetails>();
// Option 2: this will still cause N+1 if we iterate through invoices,
// because we limited the possible results using a where-condition
var invoices2 = session.QueryOver<Invoice>()
.Left.JoinAlias(i => i.Details, () => idAlias)
.Where(i => i.Number == "001")
.And(() => idAlias.Quantity > 5)
.Future<Invoice>();
// Option 3: this will work without N+1, because we don''t use a filter
// -> NHibernate will use the collection in cache
var invoices3 = session.QueryOver<Invoice>()
.Left.JoinAlias(i => i.Details, () => idAlias)
.Where(i => i.Number == "001")
.Future<Invoice>();
foreach (Invoice i in invoices)
{
int count = i.Details.Count;
}
Si comentamos dos de las tres opciones y ejecutamos el código, veremos que solo la opción 3 evitará una N + 1, las otras dos seguirán cargando los InvoiceDetails
para cada Invoice
en el bucle.
Por supuesto, este es un ejemplo muy simple y es obvio que la Opción 3 también podría ejecutarse sin la consulta base y aún así devolver el mismo resultado, pero espero que entiendas la idea.
En el caso donde cargamos dos conjuntos diferentes de entidades, es decir, la clase raíz es diferente a la Opción 1, esta "combinación" probablemente no funcionará.
Lo siento, si utilicé QueryOver en lugar de HQL, pero se aplican las mismas reglas.
Gyus, ten en cuenta que a veces puedes tener problemas similares porque LeftOuterJoin
no está configurado.
.JoinAlias (x => x.Prop, () => propAlias, JoinType.LeftOuterJoin)