7

Este es el segundo paso de una pregunta explicada here (EF 4.1 code-first: How to load related data (parent-child-grandchild)?). Por orientación de @Slauma pude recuperar datos. Mi primer código era la siguiente:EF 4.1 primer código: cómo ordenar las propiedades de navegación cuando se utilizan los métodos Incluir y/o Seleccionar?

 var model = DbContext.SitePages 
      .Where(p => p.ParentId == null && p.Level == 1) 
      .OrderBy(p => p.Order) // ordering parent 
      .ToList(); 
     foreach (var child in model) { // loading children 
      DbContext.Entry(child) 
       .Collection(t => t.Children) 
       .Query() 
       .OrderBy(t => t.Order) // ordering children 
       .Load(); 
      foreach (var grand in child.Children) { // loading grandchildren 
       DbContext.Entry(grand) 
       .Collection(t => t.Children) 
       .Query() 
       .OrderBy(t => t.Order) // ordering grandchildren 
       .Load(); 
      } 
     } 

Funciona, pero, esto enviará muchas consultas en la base de datos y yo estaba buscando una manera de hacerlo todo en una sola consulta. Por @Slauma 's orientación (que se explica en el enlace anterior) cambio la consulta a éste:

 var model2 = DbContext.SitePages 
      .Where(p => p.ParentId == null && p.Level == 1) 
      .OrderBy(p => p.Order) 
      .Include(p => p.Children // Children: how to order theme??? 
       .Select(c => c.Children) // Grandchildren: how to order them??? 
      ).ToList(); 

Ahora, ¿cómo puedo hacer un pedido hijos (y nietos) al seleccionarlos (como el primer código de arriba)?

+1

Eche un vistazo a esta pregunta: http://stackoverflow.com/questions/4156949/ef4-linq-ordering-parent-and-all-child-collections-with-eager-loading-include. Lo que estás tratando de hacer se llama "carga ansiosa", y aparentemente, no puedes usar 'OrderBy' con' Include'. – devuxer

+0

Sí, sé acerca de la carga ansiosa, y si miras el primer código (creado por el mío) verás que utilizo una declaración foreach en cada objeto en cada nivel (arriba para niño y niño para nieto) mismo como se explica en su enlace preparado. pero esto necesita más consultas en contra de la base de datos! Estoy buscando la forma de hacer esto en una sola consulta, ¡no más! –

+0

¿Puede esperar cargar toda su estructura, y luego ordenar sus vistas cuando sean necesarias? No hay casi ninguna razón por la cual debería estar goteando lógica de presentación (ordenamiento) en su lógica de acceso a datos. –

Respuesta

22

carga Desafortunadamente ansiosos (Include) no admite ningún tipo de filtrado o la clasificación de carga colecciones de niños. Hay tres opciones para lograr lo que desea:

  • Múltiples vueltas a la base de datos con carga explicite ordenada. Ese es el primer fragmento de código en su pregunta. Tenga en cuenta que las ida y vuelta múltiples no son necesariamente malas y que Include y anidados Includecan lead to huge multiplication of transfered data between database and client.

  • Uso carga ansiosos con Include o Include(....Select(....)) y ordenar los datos en la memoria después de que se cargan:

    var model2 = DbContext.SitePages 
        .Where(p => p.ParentId == null && p.Level == 1) 
        .OrderBy(p => p.Order) 
        .Include(p => p.Children.Select(c => c.Children)) 
        .ToList(); 
    
    foreach (var parent in model2) 
    { 
        parent.Children = parent.Children.OrderBy(c => c.Order).ToList(); 
        foreach (var child in parent.Children) 
         child.Children = child.Children.OrderBy(cc => cc.Order).ToList(); 
    } 
    
  • Utilice una proyección:

    var model2 = DbContext.SitePages 
        .Where(p => p.ParentId == null && p.Level == 1) 
        .OrderBy(p => p.Order) 
        .Select(p => new 
        { 
         Parent = p, 
         Children = p.Children.OrderBy(c => c.Order) 
          .Select(c => new 
          { 
           Child = c, 
           Children = c.Children.OrderBy(cc => cc.Order) 
          }) 
        }) 
        .ToList() // don't remove that! 
        .Select(a => a.Parent) 
        .ToList(); 
    

esto es sólo un único ida y vuelta y funciona si no deshabilita el seguimiento de cambios (no use .AsNoTracking() en esta consulta). Todos los objetos en esta proyección deben cargarse en el contexto (es necesario el primer ToList()) y el contexto vinculará correctamente las propiedades de navegación (una característica llamada "Intervalo de relaciones").

+0

OMG agradecimiento especial Slauma! Buen trabajo bien hecho: D: D: D Eso funciona, lo trazo y SOLO UNA consulta enviada contra db (^_^) gracias especiales querido. Acabo de cambiar el interior '' Select' con .Elija (c => nueva { Padres = c, niños = c.Children. OrdenarPor (cc => cc.Order) }) '. Gracias una y otra vez. –

+1

@ChrisMoschini: ¿Estás seguro de que importa 'ProxyCreationEnabled'? Casi nunca uso proxis de seguimiento de cambios y el lapso de relación todavía funciona. – Slauma

+0

@Slauma ¡Tienes razón! Tenemos un código que usa el Proxy desactivado y sin seguimiento para las consultas rápidas de solo lectura que no hacen este tipo de proyección; pero Proxy disabled está bien, es solo AsNoTracking() el que lanza una llave inglesa. –

0

¿Has intentado seguir?

... Select (de ch en c.children OrdenarPor ch.property Desending SELECT CH) ...

+0

¡No funciona! El error es * La expresión de la ruta Incluir debe hacer referencia a una propiedad de navegación definida en el tipo. Utilice rutas de puntos para propiedades de navegación de referencia y el operador Seleccionar para propiedades de navegación de colección. Nombre del parámetro: ruta * –

Cuestiones relacionadas