Entidad marco consulta linq Incluir () múltiples entidades secundarias

176

Esta puede ser una pregunta realmente elementry, pero ¿cuál es una buena manera de incluir varias entidades secundarias al escribir una consulta que abarca TRES niveles (o más)?

es decir, tengo 4 tablas: Company, Employee, Employee_CaryEmployee_Country

La empresa tiene una relación de 1: m con el empleado.

El empleado tiene una relación de 1: m con Employee_Car y Employee_Country.

Si quiero escribir una consulta que devuelva los datos de las 4 tablas, actualmente estoy escribiendo:

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

Tiene que haber una manera más elegante! Esto es largo aliento y genera SQL horrendo

Estoy usando EF4 con VS 2010

Nathan Liu
fuente

Respuestas:

201

Utiliza métodos de extensión . Reemplace NameOfContext con el nombre del contexto de su 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) ;
      }

}

Entonces tu código se convierte

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

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);
Nada
fuente
Pero me gustaría usarlo así: //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. Las extensiones deberían ser el primer puerto de escala para ... bueno ... extender la funcionalidad predefinida.
Venir el
12
Años después, no recomendaría las inclusiones basadas en cadenas, porque no son seguras en tiempo de ejecución. Si el nombre de la propiedad de navegación cambia o está mal escrito, se romperá. Sugerimos encarecidamente utilizar la inclusión escrita en su lugar.
Jeff Putz el
2
Desde la introducción de nameof (clase) es posible utilizar este enfoque de forma segura. En caso de que cambie el nombre de la entidad, se recogerá durante la compilación. Ejemplo: context.Companies.Include (nameof (Employee)) En caso de que uno necesite ir más abajo, los nombres deben concatenarse con nameof (Employee) + "." + Nameof (Employee_Car)
Karl
La técnica de método de extensión no funciona para consultas compiladas (al menos no en EFCore) confirmó aquí: github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge
156

EF 4.1 a EF 6

Hay un tipo fuertemente tipado.Include que permite especificar la profundidad requerida de la carga ansiosa al proporcionar expresiones Select a la profundidad adecuada:

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

El Sql generado en ambos casos todavía no es intuitivo, pero parece lo suficientemente eficiente. He puesto un pequeño ejemplo en GitHub aquí.

EF Core

EF Core tiene un nuevo método de extensión .ThenInclude(), aunque la sintaxis es ligeramente diferente :

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

Según los documentos, mantendría la 'sangría' adicional .ThenIncludepara preservar su cordura.

Información obsoleta (no haga esto):

La carga de varios nietos podría realizarse en un solo paso, pero esto requiere una reversión bastante incómoda de la copia de seguridad del gráfico antes de dirigirse hacia el siguiente nodo (NB: esto NO funciona AsNoTracking(); obtendrá un error de tiempo de ejecución):

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

Por lo tanto, me quedaría con la primera opción (un Incluir por modelo de profundidad de entidad de hoja).

StuartLC
fuente
44
Me preguntaba cómo hacerlo con sentencias fuertemente tipadas. Incluya. ¡Proyectar a los niños con Select fue la respuesta!
1
Mi equivalente de "co.Employees.Select (...)" muestra un error de sintaxis en "Seleccionar", diciendo que "'Empleados' no contiene una definición para 'Seleccionar' [o método de extensión]". He incluido System.Data.Entity. Solo quiero obtener una sola columna de la tabla unida.
Chris Walsh
1
Tenía una tabla principal que hacía referencia a la misma tabla secundaria dos veces. Con la sintaxis antigua de incluir cadenas, era difícil precargar la relación correcta. De esta manera es mucho más específico. Tenga en cuenta incluir el espacio de nombres System.Data.Entity para la inclusión fuertemente tipada.
Karl
1
Con .net core 2.1 necesitaba el espacio de nombres Microsoft.EntityFrameworkCore en lugar de System.Data.Entity
denvercoder9
27

Puede encontrar este artículo de interés que está disponible en codeplex.com .

El artículo presenta una nueva forma de expresar consultas que abarcan varias tablas en forma de formas gráficas declarativas.

Además, el artículo contiene una comparación exhaustiva del rendimiento de este nuevo enfoque con consultas EF. Este análisis muestra que GBQ supera rápidamente las consultas EF.

Merijn
fuente
¿Cómo se puede implementar esto en una aplicación del mundo real?
Victor.Uduak