Carga ansiosa de NHibernate con FetchMany solo para la subclase
Frecuentes
Visto 1,468 veces
3
Tengo una siguiente jerarquía (simplificada):
class Account
{
AccountType Type;
long Id;
string Name;
}
public enum AccountType
{
UsualAccount = 0,
SpecialAccount = 1
}
class SpecialAccount : Account
{
List<SpecialItem> SpecialItems;
}
class SpecialItem
{
long Id;
string Name;
}
Para ayudar a NHibernate a detectar la subclase SpecialItem, uso el siguiente código en el mapeo de la cuenta:
<discriminator column="Type" formula="Type"/>
y SpecialItems tienen lazy="false" establecido en el mapeo, pero que yo sepa, se ignora durante las consultas LINQ.
Ahora, cuando uso LINQ y llamo
Session.Query<Account>().ToList();
Veo que SpecialItems se obtienen en una consulta separada. Me gustaría cargarlos con ansias.
Podría hacer eso usando
Session.Query<Account>().FetchMany(a => a.SpecialItems).ToList();
pero hay un problema: SpecialItems es propiedad de SpecialAccount. Entonces, de alguna manera, necesito que FetchMany funcione solo si a es una clase SpecialAccount.
Intenté incluso algo tan feo como:
Session.Query<Account>().
FetchMany(a => (a is SpecialAccount ? (a as SpecialAccount ).SpecialItems : null));
pero NHibernate aún seleccionó SpecialItems en una consulta separada.
¿Cómo puedo hacer que NHibernate seleccione SpecialItems para SpecialAccount en una sola consulta?
Información Adicional
Estoy usando MS SQL Express 2012 y la configuración de NHibernate tiene las siguientes líneas:
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="adonet.batch_size">50</property>
<property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
NHibernate.dll versión 3.2.0.4000
Estas son las consultas generadas por NHibernate para una cuenta especial existente con Id=2, tal como aparecen en MS SQL Profiler (con eventos seleccionados RPCCompleted, Exec Prepared SQL, SQL:StmtCompleted):
select account0_.Id as Id5_, account0_.Type as Type5_, account0_.Name as Name5_, account0_.Type as clazz_ from tAccount account0_
exec sp_executesql N'SELECT specialite0_.AccountId as Id1_, specialite0_.Id as specialite1_1_, specialite0_.Id as specialite1_13_0_, specialite0_.Name as Name13_0_
FROM tSpecialItem specialite0_ WHERE specialite0_.Id=@p0',N'@p0 bigint',@p0=2
1 Respuestas
2
No se puede hacer una "obtención condicional". Necesitas al menos dos consultas.
Sin embargo, puede agruparlos usando un Futuro Consulta LINQ, por lo que sería efectivamente un viaje de ida y vuelta:
session.Query<SpecialAccount>().FetchMany(x => x.SpecialItems).ToFuture();
var accounts = session.Query<Account>().ToFuture();
Ambas consultas se ejecutan cuando enumera accounts
. No almacenamos los resultados de la primera consulta. explícitamente, Pero el SpecialAccounts
se cargan en memoria con sus correspondientes SpecialItems
colecciones, por lo que no hay llamadas DB adicionales.
Respondido 28 ago 12, 15:08
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas nhibernate subclass eager-loading or haz tu propia pregunta.
Gracias, ToFuture parece una buena idea, pero por alguna razón no cambió el SQL generado. Profiler aún muestra dos consultas "exec sp_executesql". Tal vez ToFuture no funcione con Fetch como se esperaba (como dicen aquí stackoverflow.com/questions/5143480/… ) ... - soloamartin
Funciona en MyMachine™, utilizando NH 3.3.1 y SQL2008. Solo una consulta enviada a la base de datos (y no tiene sp_executesql). ¿Quieres publicar el resultado de tu generador de perfiles como una edición de tu pregunta? - Diego Mijelshon
Se agregó más información sobre mi configuración y las consultas en SQL Profiler. - soloamartin
Las consultas que está utilizando son diferentes de las que proporcioné. ¿Podría publicar sus consultas, además del SQL resultante? - Diego Mijelshon
En realidad, mis clases tienen algunos campos más (solo tipos CLR simples), así que limpié las consultas para mantener el mínimo. Probé el código LINQ casi exactamente como publicaste y agregaste cuentas.ToList() para activar el SQL, pero aún veo dos consultas en el generador de perfiles. Supongo que tendré que crear un proyecto completamente limpio desde cero para que coincida con mi ejemplo simplificado, para poder ver mejor qué podría ser diferente. - soloamartin