when - Entidad framework linq query Include() mĂșltiples entidades hijas
linq include vs join (4)
Esta puede ser una pregunta realmente elemental, pero ¿cuál es una buena manera de incluir múltiples entidades secundarias al escribir una consulta que abarca TRES niveles (o más)?
es decir, tengo 4 tablas: Company
, Employee
, Employee_Car
y Employee_Country
La compañía tiene una relación 1: m con el empleado.
El empleado tiene una relación 1: m con Employee_Car y Employee_Country.
Si quiero escribir una consulta que devuelva los datos de las 4 tablas, 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
Puede encontrar este artículo de interés que está disponible en codeplex.com .
- Mejora del rendimiento de la consulta del marco de la entidad mediante consultas basadas en gráficos .
El artículo presenta una nueva forma de expresar consultas que abarcan múltiples tablas en forma de formas de gráfico declarativo.
Además, el artículo contiene una comparación exhaustiva del rendimiento de este nuevo enfoque con las consultas de EF. Este análisis muestra que GBQ supera rápidamente las consultas de EF.
Usa 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);
No hay otra manera, excepto la implementación de la carga diferida.
O carga manual ....
myobj = context.MyObjects.First();
myobj.ChildA.Load();
myobj.ChildB.Load();
...
EF 4.1 a EF 6
Existe un .Include
fuertemente tipado que permite especificar la profundidad requerida de la carga ansiosa proporcionando 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)
...
De acuerdo con los documentos, mantendría la ''sangría'' adicional en el .ThenInclude
para preservar su cordura.
Información obsoleta (no haga esto):
La carga de múltiples nietos se puede hacer en un solo paso, pero esto requiere una reversión bastante torpe para hacer una copia de seguridad del gráfico antes de dirigirse al siguiente nodo (NB: esto NO funciona con 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);
Así que me quedaría con la primera opción (un incluir por modelo de profundidad de entidad de hoja).