Estrutura de entidades - Incluir vários níveis de propriedades

376

O método Include () funciona muito bem para listas de objetos. Mas e se eu precisar subir dois níveis? Por exemplo, o método abaixo retornará ApplicationServers com as propriedades incluídas mostradas aqui. No entanto, ApplicationsWithOverrideGroup é outro contêiner que contém outros objetos complexos. Posso fazer um Include () nessa propriedade também? Ou como posso obter essa propriedade para carregar totalmente?

Como está agora, este método:

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

Preenche apenas a propriedade Enabled (abaixo) e não as propriedades Application ou CustomVariableGroup (abaixo). Como faço isso acontecer?

public class ApplicationWithOverrideVariableGroup : EntityBase
{
    public bool Enabled { get; set; }
    public Application Application { get; set; }
    public CustomVariableGroup CustomVariableGroup { get; set; }
}
Bob Horn
fonte
Oi, Por que eu recebo uma exceção Expression must be a member expressionquando eu tente o seguinte: Para incluir uma coleção e, em seguida, uma coleção um nível abaixo: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection)).
Joe.wang
11
@ BobHorn, eu tenho o mesmo problema .. No meu caso, o aninhamento vai fundo várias camadas, eu consegui fazer uma inclusão que você apontou. No SQL que foi gerado, eu pude ver todas as colunas retornando com um nome alternativo diferente como c1, c2, algo assim. Minha pergunta é: como posso formar uma coleção aninhada de DTO com todas as minhas inclusões :( .. Pode ser que você possa tomar o exemplo acima, pois retornamos todas as colunas sem nenhum DTO personalizado (que é uma coleção de DTOs )
TechQuery

Respostas:

704

Para EF 6

using System.Data.Entity;

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

Certifique-se de adicionar using System.Data.Entity;para obter a versão Includeque aceita uma lambda.


Para EF Core

Use o novo método ThenInclude

query.Include(x => x.Collection)
     .ThenInclude(x => x.Property);
Diego Torres
fonte
11
Não consigo incluir () no ApplicationsWithOverrideGroup. Não aparece no intellisense.
Bob Horn
Não posso usar sua edição porque ApplicationsWithOverrideGroup é uma lista. Aplicativo é uma propriedade em cada item da lista, não na própria lista.
Bob Horn
11
Ahhhh, mas esse link que você forneceu parece fornecer a resposta. Deixe-me tentar o seguinte: Para incluir uma coleção e, em seguida, uma coleção um nível abaixo: query.Include (e => e.Level1Collection.Select (l1 => l1.Level2Collection)).
Bob Horn
60
Lembre-se de incluir System.Data.Entity nos usos. Caso contrário, o Intellisense fornecerá apenas a versão Include (caminho da string) do método.
JO Raqueño 28/07
5
@Adeem, você precisa ligar Includepara cada propriedade:Db.States.Include(state => state.Cities.Select(city => city.Customers).Include(state => state.Cities.Select(city => city.Vendors)
Diego Torres
72

Se bem entendi, você está perguntando sobre a inclusão de propriedades aninhadas. Se então :

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

ou

.Include("ApplicationsWithOverrideGroup.NestedProp")  

ou

.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")  
Judo
fonte
6
Obrigado, eu posso tentar isso. Eu esperava poder manter as coisas fortemente digitadas e evitar literais de string. Mas se é assim que tem que ser feito ...
Bob Horn
11
Você estava perto. Talvez não esteja claro que ApplicationsWithOverrideGroup era uma lista. Obrigado por ajudar!
Bob Horn
@ Judô, eu tenho o mesmo problema .. No meu caso, o aninhamento vai fundo várias camadas, eu consegui fazer uma inclusão que você apontou. No SQL que foi gerado, eu pude ver todas as colunas retornando com um nome alternativo diferente como c1, c2, algo assim. Minha pergunta é: como posso formar uma coleção aninhada de DTO com todas as minhas inclusões :( .. Pode ser que você possa tomar o exemplo acima, pois retornamos todas as colunas sem nenhum DTO personalizado (que é uma coleção de DTOs )
TechQuery
2
Lembre-se de incluir System.Data.Entity nos usos. Caso contrário, o Intellisense fornecerá apenas a Include(string path)versão do método.
21918 AlexMelw
53

EF Core: usando "ThenInclude" para carregar vários níveis: por exemplo:

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
    .ToList();
thangcao
fonte
53
Parece que este é apenas o núcleo EF
Chris Marisic
27
Para sua informação: VS2017, o intellisense não estava funcionando. Basta digitar como você acha que deveria ser e o realce do erro deve desaparecer.
precisa saber é o seguinte
4
Quero enfatizar o comentário de @JohnWrensby, às vezes o Intellisense pode demorar especialmente para lidar com esses ThenInclude, isso pode ser bastante confuso para novos usuários. Eu também tive casos em que a expressão simples incluir lambda não foi tratada adequadamente, até você digitar e compilar, ignorando os "erros" mostrados no VS.
Pac0
@ Pac0 você salvou meu dia. lutando para ver os itens filhos e não podia.
Bendram
28

Fiz uma pequena ajuda para o Entity Framework 6 (estilo .Net Core), para incluir subentidades de uma maneira agradável.

Agora está no 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();

O pacote está disponível no GitHub .

Lenny32
fonte
oi, eu tenho uma exceção em tempo de execução, não é possível converter IncludableQueryable <observablecollection> para IncludableQueryable <genericcollection>
user2475096
Estou usando o db primeiro e modifiquei o arquivo tt para obter ObservableCollections para todas as minhas entidades, qualquer ajuda é bem-vinda.
user2475096
2
@ lenny32 algo para conhecer com esta extensão?
Aaron Hudon
Observe que isso não é necessário se a propriedade para a qual você está navegando é individual com o DbSet de onde você navegou, e você pode encadear DbSet<One>().Include(x => x.Two.Three.Four.Five.Six)com o único inconveniente de estar computando um produto cartesiano e potencialmente aumentando a largura de banda.
John Zabroski
23

Mais exemplos de EFCore no MSDN mostram que você pode fazer coisas bastante complexas com Includee ThenInclude.

Este é um bom exemplo de quão complexo você pode ficar (esta é uma afirmação!):

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();

Veja como você pode encadear Includemesmo depois ThenIncludee isso meio que 'redefine' você de volta ao nível da entidade de nível superior (Instrutores).

Você pode até repetir a mesma coleção de 'primeiro nível' (CourseAssignments) várias vezes seguidas de ThenIncludescomandos separados para acessar diferentes entidades filhas .

Observe que sua consulta real deve ser marcada no final da cadeia Includeou ThenIncludes. O seguinte NÃO funciona:

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

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

É altamente recomendável que você configure o log e verifique se suas consultas não estão fora de controle se você incluir mais de uma ou duas coisas. É importante ver como ele realmente funciona - e você notará que cada 'inclusão' separada é normalmente uma nova consulta para evitar junções em massa que retornam dados redundantes.

AsNoTracking pode acelerar bastante as coisas, se você não pretender realmente editar as entidades e salvar novamente.

Simon_Weaver
fonte
Existe uma maneira de obter as matrículas e os departamentos sem a repetição. Inclui aulas e tarefas? (Até agora, parece que a API pode ir mais fundo com .ThenInclude, ou de volta para o nível superior com .include, mas não há nada a estadia no mesmo nível?)
William Jockusch
Se você deseja carregar preguiçosamente, fique atento ao blog EF Core 2.1 . Não tenho certeza do que você está pensando - não exige muito mais para fazer isso e reduz bastante o que retorna do banco de dados. Uma entidade pode ter apenas um ou dois itens de 'mesmo nível', mas também pode ter 50 para um projeto grande, sendo explícito torna seu aplicativo muito mais rápido.
21918 Simon_Weaver
Essa foi uma boa explicação do conceito de Incluir "redefinindo" o nível novamente para o nível inicial. Ajudou-me a entender a hierarquia do sistema de inclusão. Felicidades!
AFM-Horizon
22

Eu também tive que usar várias inclusões e no terceiro nível eu precisava de várias propriedades

(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();

Isso pode ajudar alguém :)

dnxit
fonte
11
isso pode ser feito sem repetir.Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt......
Multinerd
bem, depende, quão fundo você quer ir
dnxit
7

Deixe-me declarar claramente que você pode usar a sobrecarga de string para incluir níveis aninhados, independentemente das multiplicidades dos relacionamentos correspondentes, se você não se importa em usar literais de string:

query.Include("Collection.Property")
Mrmashal
fonte
11
Este método foi útil para eu descobrir como isso pode ser codificado em VB, como não consigo encontrar em nenhum lugar após horas pesquisando no Google.
Coder
Isso funciona muito bem para mim, eu uso muito isso !!! Até funciona em conjunto com as instruções .SelectMany:query.SelectMany(x=>x.foos).Include("bar").Include("bar.docs")...
Ephie