Entity Framework: incluye múltiples niveles de propiedades

376

El método Include () funciona bastante bien para listas en objetos. ¿Pero qué pasa si necesito ir a dos niveles de profundidad? Por ejemplo, el siguiente método devolverá ApplicationServers con las propiedades incluidas que se muestran aquí. Sin embargo, ApplicationsWithOverrideGroup es otro contenedor que contiene otros objetos complejos. ¿Puedo hacer un Incluir () en esa propiedad también? ¿O cómo puedo hacer que esa propiedad se cargue completamente?

Tal como está ahora, este método:

public IEnumerable<ApplicationServer> GetAll()
{
    return this.Database.ApplicationServers
        .Include(x => x.ApplicationsWithOverrideGroup)                
        .Include(x => x.ApplicationWithGroupToForceInstallList)
        .Include(x => x.CustomVariableGroups)                
        .ToList();
}

Rellenará solo la propiedad Enabled (a continuación) y no las propiedades Application o CustomVariableGroup (a continuación). ¿Cómo hago que esto suceda?

public class ApplicationWithOverrideVariableGroup : EntityBase
{
    public bool Enabled { get; set; }
    public Application Application { get; set; }
    public CustomVariableGroup CustomVariableGroup { get; set; }
}
Bob Horn
fuente
Hola, ¿Por qué recibo una excepción Expression must be a member expressioncuando intento esto: Para incluir una colección y luego un nivel de colección de uno abajo: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection)).
Joe.wang
1
@BobHorn, tengo el mismo problema ... En mi caso, el anidamiento se profundiza en varias capas, logré hacer una inclusión que usted señaló. En el SQL que se generó, pude ver que todas las columnas están regresando con diferentes alias como c1, c2, algo así. Mi pregunta es, ¿cómo puedo formar una colección DTO anidada de todas mis inclusiones? )
TechQuery

Respuestas:

705

Para EF 6

using System.Data.Entity;

query.Include(x => x.Collection.Select(y => y.Property))

Asegúrate de agregar using System.Data.Entity;para obtener la versión Includeque toma una lambda.


Para EF Core

Usa el nuevo método ThenInclude

query.Include(x => x.Collection)
     .ThenInclude(x => x.Property);
Diego Torres
fuente
1
No puedo hacer Incluir () en ApplicationsWithOverrideGroup. No aparece en intellisense.
Bob Horn
No puedo usar su edición porque ApplicationsWithOverrideGroup es una Lista. La aplicación es una propiedad en cada elemento de la lista, no en la lista misma.
Bob Horn
1
Ahhhh, pero ese enlace que proporcionó parece proporcionar la respuesta. Permítanme probar esto: para incluir una colección y luego una colección de un nivel inferior: query.Include (e => e.Level1Collection.Select (l1 => l1.Level2Collection)).
Bob Horn
6060
Recuerde incluir System.Data.Entity en los usos. De lo contrario, Intellisense solo le dará la versión Incluir (ruta de cadena) del método.
OJ Raqueño
55
@Adeem, debe llamar Includepara cada propiedad:Db.States.Include(state => state.Cities.Select(city => city.Customers).Include(state => state.Cities.Select(city => city.Vendors)
Diego Torres
72

Si te entiendo correctamente, estás preguntando sobre la inclusión de propiedades anidadas. Si es así :

.Include(x => x.ApplicationsWithOverrideGroup.NestedProp)

o

.Include("ApplicationsWithOverrideGroup.NestedProp")  

o

.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")  
Judo
fuente
66
Gracias, puedo intentar eso. Tenía la esperanza de poder mantener las cosas fuertemente tipadas y evitar los literales de cadena. Pero si así es como debe hacerse ...
Bob Horn
1
Estabas cerca Puede que no haya sido claro que ApplicationsWithOverrideGroup era una lista. ¡Gracias por ayudar!
Bob Horn
@Judo, tengo el mismo problema ... En mi caso, el anidamiento se profundiza en varias capas, logré hacer una inclusión que usted señaló. En el SQL que se generó, pude ver que todas las columnas están regresando con diferentes alias como c1, c2, algo así. Mi pregunta es, ¿cómo puedo formar una colección DTO anidada de todas mis inclusiones? )
TechQuery
2
Recuerde incluir System.Data.Entity en los usos. De lo contrario, Intellisense solo le dará la Include(string path)versión del método.
AlexMelw
53

EF Core: Usando "ThenInclude" para cargar niveles múltiples: Por ejemplo:

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
    .ToList();
thangcao
fuente
53
Parece que esto es solo EF Core
Chris Marisic
27
FYI: VS2017 el intellisense no estaba funcionando para. EntoncesIncluir. Simplemente escríbalo como cree que debería ser y el resaltado de error debería desaparecer.
JohnWrensby
44
Quiero enfatizar el comentario de @JohnWrensby, el Intellisense a veces puede tomar mucho tiempo para manejar estos ThenInclude, esto puede ser bastante confuso para los nuevos usuarios. También tuve casos en los que la expresión simple Incluir lambda no se manejó correctamente, hasta que simplemente la escribiste y la compilas, ignorando los "errores" que se muestran en VS.
Pac0
@ Pac0 me salvaste el día. luchando por ver los artículos infantiles y no pudo.
Bendram
28

Hice un pequeño ayudante para Entity Framework 6 (.Net Core style), para incluir subentidades de una manera agradable.

Ahora está en NuGet: Install-Package ThenInclude.EF6

using System.Data.Entity;

var thenInclude = context.One.Include(x => x.Twoes)
    .ThenInclude(x=> x.Threes)
    .ThenInclude(x=> x.Fours)
    .ThenInclude(x=> x.Fives)
    .ThenInclude(x => x.Sixes)
    .Include(x=> x.Other)
    .ToList();

El paquete está disponible en GitHub .

Lenny32
fuente
hola, tengo una excepción en tiempo de ejecución, no puedo enviar IncludableQueryable <observablecollection> a IncludableQueryable <genericcollection>
user2475096
Estoy usando db primero y he modificado el archivo tt para obtener ObservableCollections para todas mis entidades, cualquier ayuda es bienvenida.
user2475096
2
@ lenny32 ¿hay algo a tener en cuenta con esta extensión?
Aaron Hudon
Tenga en cuenta que esto no es necesario si la propiedad a la que está navegando es uno a uno con el DbSet desde el que navegó, y puede encadenar DbSet<One>().Include(x => x.Two.Three.Four.Five.Six)con el único inconveniente de que está calculando un producto cartesiano y potencialmente aumentando el ancho de banda.
John Zabroski
23

Más ejemplos de EFCore en MSDN muestran que puede hacer algunas cosas bastante complejas con Includey ThenInclude.

Este es un buen ejemplo de lo complejo que puede ser (¡esta es una sola declaración!):

viewModel.Instructors = await _context.Instructors

      .Include(i => i.OfficeAssignment)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)

      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

Vea cómo puede encadenar Includeincluso después ThenIncludey de alguna manera lo 'restablece' al nivel de la entidad de nivel superior (Instructores).

Incluso puede repetir la misma colección de 'primer nivel' (Asignaciones de curso) varias veces, seguido de ThenIncludescomandos separados para llegar a diferentes entidades secundarias.

Tenga en cuenta que su consulta real debe estar etiquetada en el final de la cadena Includeo ThenIncludes. Lo siguiente NO funciona:

var query = _context.Instructors.AsQueryable();
query.Include(i => i.OfficeAssignment);

var first10Instructors = query.Take(10).ToArray();

Le recomiendo que configure el registro y asegúrese de que sus consultas no estén fuera de control si incluye más de una o dos cosas. Es importante ver cómo funciona realmente, y notará que cada 'inclusión' por separado suele ser una nueva consulta para evitar uniones masivas que devuelven datos redundantes.

AsNoTracking puede acelerar mucho las cosas si no tiene la intención de editar las entidades y volver a guardarlas.

Simon_Weaver
fuente
¿Hay alguna manera de obtener tanto la inscripción como los departamentos sin su repetición. Incluye curso Asignación y curso? (Hasta ahora, parece que la Api puede profundizar con .ThenInclude, o volver al nivel superior con .Include, pero ¿no hay nada para permanecer en el mismo nivel?)
William Jockusch
Si desea una carga lenta, esté atento a EF Core 2.1 blogs.msdn.microsoft.com/dotnet/2018/02/02/… pero si solo desea cargar más al mismo nivel, creo que esto es por diseño. No estoy seguro de lo que está pensando: no requiere mucho más para hacer esto y reduce en gran medida lo que regresa de la base de datos. Una entidad puede tener solo una o dos cosas del 'mismo nivel' pero también puede tener 50 para un proyecto grande, siendo explícito hace que su aplicación sea mucho más rápida.
Simon_Weaver
Esta fue una buena explicación del concepto de Incluir "restablecer" el nivel nuevamente al nivel inicial. Me ayudó a comprender la jerarquía del sistema includ. ¡Salud!
AFM-Horizon
22

También tuve que usar varias inclusiones y en el 3er nivel necesitaba múltiples propiedades

(from e in context.JobCategorySet
                      where e.Id == id &&
                            e.AgencyId == agencyId
                      select e)
                      .Include(x => x.JobCategorySkillDetails)
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.DurationType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RuleType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RateType))
                      .FirstOrDefaultAsync();

Esto puede ayudar a alguien :)

dnxit
fuente
1
¿Se puede hacer esto sin repetir?.Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt......
Multinerd
bueno, depende, qué tan profundo quieres ir
dnxit
7

Permítame decirle claramente que puede usar la sobrecarga de cadena para incluir niveles anidados independientemente de las multiplicidades de las relaciones correspondientes, si no le importa usar literales de cadena:

query.Include("Collection.Property")
mrmashal
fuente
1
Este método fue útil para mí para descubrir cómo se puede codificar esto en VB, ya que no puedo encontrarlo después de horas de googlear.
Codificador
Esto funciona muy bien para mí, ¡lo uso mucho! Incluso funciona combinado con .SelectMany declaraciones:query.SelectMany(x=>x.foos).Include("bar").Include("bar.docs")...
Ephie