Consulta linq da estrutura da entidade Include () várias entidades filhas

176

Essa pode ser uma pergunta realmente elementar, mas qual é uma boa maneira de incluir várias entidades filhas ao escrever uma consulta que abrange três níveis (ou mais)?

ou seja, eu tenho 4 mesas: Company, Employee, Employee_CareEmployee_Country

A empresa tem um relacionamento de 1: m com Funcionário.

O funcionário tem um relacionamento de 1: m com Employee_Car e Employee_Country.

Se eu quiser escrever uma consulta que retorne os dados de todas as 4 tabelas, atualmente estou escrevendo:

Company company = context.Companies
                         .Include("Employee.Employee_Car")
                         .Include("Employee.Employee_Country")
                         .FirstOrDefault(c => c.Id == companyID);

Tem que haver uma maneira mais elegante! Isso é longo e gera SQL horrendo

Estou usando o EF4 com o VS 2010

Nathan Liu
fonte

Respostas:

201

Use métodos de extensão . Substitua NameOfContext pelo nome do seu contexto de objeto.

public static class Extensions{
   public static IQueryable<Company> CompleteCompanies(this NameOfContext context){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country") ;
     }

     public static Company CompanyById(this NameOfContext context, int companyID){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country")
             .FirstOrDefault(c => c.Id == companyID) ;
      }

}

Então seu código se torna

     Company company = 
          context.CompleteCompanies().FirstOrDefault(c => c.Id == companyID);

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);
Nix
fonte
Mas eu gostaria de usá-lo assim: //inside public static class Extensions public static IQueryable<Company> CompleteCompanies(this DbSet<Company> table){ return table .Include("Employee.Employee_Car") .Include("Employee.Employee_Country") ; } //code will be... Company company = context.Companies.CompleteCompanies().FirstOrDefault(c => c.Id == companyID); //same for next advanced method
Hamid
Bullsye Nix. As extensões devem ser a primeira porta de escala para ... bem ... estender a funcionalidade predefinida.
ComeIn
12
Anos depois, eu não recomendaria as inclusões baseadas em string, porque elas não são seguras em tempo de execução. Se o nome da propriedade de navegação mudar ou for digitado incorretamente, ele será interrompido. Sugira enfaticamente o uso da inclusão digitada.
Jeff Putz
2
desde a introdução do nome da classe, é possível usar essa abordagem com segurança. Caso o nome da entidade mude, ele será escolhido durante a compilação. "": Exemplo context.Companies.Include (nameof (Employee)) No caso da pessoa precisa ir mais longe nomes tem que concatent com nameof (Employee) + + nameof (Employee_Car)
Karl
A técnica do método de extensão não funciona para consultas compiladas (pelo menos não no EFCore) confirmadas aqui: github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge
156

EF 4.1 a EF 6

Há uma digitação forte.Include que permite que a profundidade exigida do carregamento mais rápido seja especificada fornecendo expressões Select à profundidade apropriada:

using System.Data.Entity; // NB!

var company = context.Companies
                     .Include(co => co.Employees.Select(emp => emp.Employee_Car))
                     .Include(co => co.Employees.Select(emp => emp.Employee_Country))
                     .FirstOrDefault(co => co.companyID == companyID);

O SQL gerado em ambas as instâncias ainda não é intuitivo, mas parece ter desempenho suficiente. Eu coloquei um pequeno exemplo no GitHub aqui

EF Core

O EF Core possui um novo método de extensão .ThenInclude(), embora a sintaxe seja um pouco diferente :

var company = context.Companies
                     .Include(co => co.Employees)
                           .ThenInclude(emp => emp.Employee_Car)
                      ...

De acordo com os documentos, eu manteria o 'travessão' extra .ThenIncludepara preservar sua sanidade.

Informações obsoletas (não faça isso):

O carregamento de vários netos pode ser feito em uma única etapa, mas isso requer uma reversão bastante embaraçosa do backup antes de ir para o próximo nó (NB: Isso NÃO funciona com AsNoTracking()- você receberá um erro de execução):

var company = context.Companies
         .Include(co => 
             co.Employees
                .Select(emp => emp.Employee_Car
                    .Select(ec => ec.Employee)
                    .Select(emp2 => emp2.Employee_Country)))
         .FirstOrDefault(co => co.companyID == companyID);

Então, eu ficaria com a primeira opção (uma opção Incluir por modelo de profundidade de entidade em folha).

StuartLC
fonte
4
Eu queria saber como fazê-lo com instruções .Include fortemente digitadas. Projetar as crianças com o Select foi a resposta!
1
Meu equivalente de "co.Employees.Select (...)" mostra um erro de sintaxe em "Selecionar", dizendo que "'Funcionários' não contém uma definição para 'Selecionar' [ou método de extensão]". Eu incluí System.Data.Entity. Eu só quero obter uma única coluna da tabela unida.
Chris Walsh
1
Eu tinha uma tabela pai que fazia referência à mesma tabela filho duas vezes. Com a sintaxe de inclusão da cadeia antiga, era difícil pré-carregar o relacionamento correto. Dessa forma, é muito mais específico. Lembre-se de incluir o espaço para nome System.Data.Entity para incluir fortemente digitado.
Karl
1
Com núcleo .NET 2.1 Eu precisava do namespace Microsoft.EntityFrameworkCore vez de System.Data.Entity
denvercoder9
27

Você pode encontrar este artigo de interesse, disponível em codeplex.com .

O artigo apresenta uma nova maneira de expressar consultas que abrangem várias tabelas na forma de formas de gráfico declarativas.

Além disso, o artigo contém uma comparação completa de desempenho dessa nova abordagem com as consultas da EF. Essa análise mostra que o GBQ supera rapidamente as consultas EF.

Merijn
fonte
como isso pode ser implementado em um aplicativo do mundo real?
precisa saber é o seguinte