2010-06-29 16 views
22

que tiene un requisito para cargar un objeto complejo llamado Nodo ... así que no es tan compleja ... se ve como sigue: -Cargando Eager Usando Fluido NHibernate/Nhibernate y Automapping

Un Nodo tiene una referencia a EntityType que tiene una uno a muchos con propiedad que a su vez tiene un uno a muchos con PorpertyListValue

public class Node 
{ 
    public virtual int Id 
    { 
     get; 
     set; 
    } 

    public virtual string Name 
    { 
     get; 
     set; 
    } 

    public virtual EntityType Etype 
    { 
     get; 
     set; 
    } 

} 


public class EntityType 
{ 
    public virtual int Id 
    { 
     get; 
     set; 
    } 

    public virtual string Name 
    { 
     get; 
     set; 
    } 

    public virtual IList<Property> Properties 
    { 
     get; 
     protected set; 
    } 

    public EntityType() 
    { 
     Properties = new List<Property>(); 
    } 
} 

public class Property 
{ 
    public virtual int Id 
    { 
     get; 
     set; 
    } 

    public virtual string Name 
    { 
     get; 
     set; 
    }   

    public virtual EntityType EntityType 
    { 
     get; 
     set; 
    } 

    public virtual IList<PropertyListValue> ListValues 
    { 
     get; 
     protected set; 
    } 

    public virtual string DefaultValue 
    { 
     get; 
     set; 
    } 

    public Property() 
    { 
     ListValues = new List<PropertyListValue>(); 
    } 
} 


public class PropertyListValue 
{ 
    public virtual int Id 
    { 
     get; 
     set; 
    } 

    public virtual Property Property 
    { 
     get; 
     set; 
    } 

    public virtual string Value 
    { 
     get; 
     set; 
    } 

    protected PropertyListValue() 
    { 
    } 
} 

Lo que intento hacer es cargar el objeto Node con todos los objetos secundarios, todo a la vez. Sin carga lenta. La razón es que tengo miles de objetos Node en la base de datos y tengo que enviarlos por cable usando el Servicio WCF. Encontré el problema de las clases SQL N + 1. Estoy usando Fluent Nhibernate con Automapping y NHibernate Profiler me sugirió que use FetchMode.Eager para cargar los objetos completos a la vez. Estoy utilizando el siguiente qyuery

 Session.CreateCriteria(typeof (Node)) 
      .SetFetchMode("Etype", FetchMode.Join) 
      .SetFetchMode("Etype.Properties", FetchMode.Join) 
      .SetFetchMode("Etype.Properties.ListValues", FetchMode.Join) 

o el uso de NHibernate LINQ

 Session.Linq<NodeType>() 
     .Expand("Etype") 
     .Expand("Etype.Properties") 
     .Expand("Etype.Properties.ListValues") 

Cuando corro cualquiera de la consulta anterior, ambos generan una misma consulta individual con toda la externa izquierda se une, que es lo Necesito. Sin embargo, por alguna razón, el retorno IList de la consulta no se está cargando propiedad en los objetos. De hecho, el conteo de Nodos devueltos es igual al número de filas de la consulta, por lo que los objetos Nodos se repiten. Además, las propiedades dentro de cada Nodo se repiten, y también lo hacen los Valores de lista.

Así que me gustaría saber cómo modificar la consulta anterior para devolver todos los Nodos únicos con las propiedades y valores de lista dentro de ellos.

Gracias Nabeel

+0

en Google me enteré de DistinctRootEntityResultTransformer pero que solo resuelve el problema para los objetos Root. Todavía estoy recibiendo duplicados en las colecciones secundarias. Cada objeto raíz en la lista devuelta tiene algún raro desorden producto cartesiano en el niño colecciones con varias instancias de la misma entidad. ¿Alguna idea? A la espera de Nabeel – nabeelfarid

+1

Creo que he encontrado la solución, pero me gustaría saber si es el correcto. Las colecciones hijas (EType.Properties, Etype.Properties.ListValues) objeto raíz en el interior (nodo) son IList. Y he leído en la documentación que IList puede contener duplicados, así que si cambio IList a IColoque/ ICollection, la consulta no se carga instancias duplicadas dentro las colecciones hijas. Pero esta solución requiere mucha refactorización. Me gustaría saber si hay una manera de lograr lo mismo usando IList para niños colecciones? espera, Nabeel – nabeelfarid

+1

Tengo el mismo problema (utilizando Fetchmode.Eager). Estoy bastante decepcionado con NHibernate por esto. Prefiero tener un error que datos incorrectos. – UpTheCreek

Respuesta

13

lo descubra a mí mismo. La clave es usar SetResultTransformer() pasando un objeto de DistinctRootEntityResultTransformer como parámetro.Así que la pregunta ahora se ve como la siguiente manera

Session.CreateCriteria(typeof (Node)) 
    .SetFetchMode("Etype", FetchMode.Join) 
    .SetFetchMode("Etype.Properties", FetchMode.Join) 
    .SetFetchMode("Etype.Properties.ListValues", FetchMode.Join) 
    .SetResultTransformer(new DistinctRootEntityResultTransformer()); 

encontré la respuesta a mis preguntas a través de estos enlaces:

http://www.mailinglistarchive.com/html/[email protected]/2010-05/msg00512.html

http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx

+4

+1, pero Wow eso es feo. Esto parece un desastre. No estoy impresionado con Nibernate por esto. ¿Por qué deberíamos necesitar transformar el resultado? Parece que NH no está haciendo su trabajo correctamente. – UpTheCreek

21

cada asignación tiene que tener la carga diferida de

en el Nodo Mapa:

Map(x => x.EntityType).Not.LazyLoad(); 

en EnityType Mapa:

Map(x => x.Properties).Not.LazyLoad(); 

y así sucesivamente ..

Además, vea NHibernate Eager loading multi-level child objects durante un tiempo de carga ansiosa

Agregado:

información adicional sobre SQL N + 1:

http://nhprof.com/Learn/Alerts/SelectNPlusOne

+2

Gracias por la respuesta de Tim, Bueno, yo no desee establecer el .Not.LazyLoad() en el mapeo, ya que luego se convertirá en el comportamiento por defecto, y en mi aplicación tengo un servicio WCF que que necesita para pasar los datos a el cliente y yo queremos cargar todos los datos a la vez en una sola consulta para evitar el escenario SQL N + 1 (http://nhprof.com/Learn/Alerts/SelectNPlusOne). El resto de la aplicación no requiere eagerloading. Entonces, ¿alguna idea de cómo puedo abordar este escenario? – nabeelfarid

+1

También tengo entendido que .Not.LazyLoad no resuelve el problema de SQL N + 1. Fro mNhibernate perfilador me he dado cuenta de que a pesar de que carga todos todo el gráfico de objetos de una sola vez, todavía genera más de una consulta, una consulta para cada objeto de referencia/hasMany, que no quiero porque tengo thosands de nodos con hundered de entitytypes y properties y no quiero que se generen estas consultas únicas. Nabeel – nabeelfarid

+0

pensé que quería que asigna de esa manera. Agregué un enlace a otra pregunta que muestra una carga ansiosa en una instancia particular. Creo que esto te ayudará a producir una unión. De lo contrario, podría considerar corregir un procedimiento almacenado y asignarlo como una consulta con nombre. Ver el código carteles originales en http://stackoverflow.com/questions/1637862/fluent-nhibernate-and-stored-procedures para un ejemplo de que –

4

SetResultTransformer con DistinctRootEntityResultTransformer sólo funcionará para el objeto principal, pero Las colecciones IList se multiplicarán.

+0

eso es correcto. Uno tiene que usar ISet o ICollection – nabeelfarid

+0

¿Cómo usarás ISet o ICollection? –

8

que terminó con algo como esto:

HasMany(x => x.YourList).KeyColumn("ColumnName").Inverse().Not.LazyLoad().Fetch.Join() 

Sólo asegúrese de seleccionar su entidad de este tipo, para evitar la duplicación debido a la unión:

session.CreateCriteria(typeof(T)).SetResultTransformer(Transformers.DistinctRootEntity).List<T>(); 
+0

¿Puede hacer esto con referencias también? – aggietech

Cuestiones relacionadas