Carga ansiosa de NHibernate con FetchMany solo para la subclase

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

preguntado el 28 de agosto de 12 a las 10:08

1 Respuestas

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

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

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.