实体框架linq查询Include()多个子实体


176

这可能是一个真正的基本问题,但是在编写跨越三个级别(或更多级别)的查询时,包括多个子实体的一种好方法是什么?

即我有4个表:CompanyEmployeeEmployee_CarEmployee_Country

公司与员工的关系为1:m。

Employee与Employee_Car和Employee_Country的关系为1:m。

如果我想编写一个查询来返回所有4个表中的数据,我当前正在编写:

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

必须有一种更优雅的方式!这很长一段时间,并产生可怕的SQL

我在VS 2010中使用EF4

Answers:


201

使用扩展方法。用对象上下文的名称替换NameOfContext

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

}

然后你的代码变成

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

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);

但我想这样使用它: //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
哈米德

Bullsye Nix。扩展应该是……扩展预定义功能的第一个调用端口。
ComeIn

12
多年以后,我不建议您使用基于字符串的包含,因为它们在运行时不安全。如果导航属性名称更改或拼写错误,则它将中断。强烈建议改用键入的包含。
杰夫·普茨

2
由于引入了nameof(class),因此可以安全地使用此方法。如果实体名称更改,则将在编译期间将其拾取。例如:context.Companies.Include(nameof(Employee))如果需要进一步说明,姓名必须与nameof(Employee)+“ ..” + nameof(Employee_Car)相匹配
卡尔

扩展方法技术不适用于编译查询的工作(至少不是EFCore)证实这里:github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge

156

EF 4.1至EF 6

有一个强类型.Include可以通过提供适当深度的Select表达式来指定所需的急切加载深度:

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

在这两种情况下生成的Sql仍然不是很直观,但看起来足够高效。我在这里GitHub上放了一个小例子

EF核心

EF Core具有新的扩展方法.ThenInclude(),尽管语法略有不同

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

根据文档,我会保留额外的“缩进” .ThenInclude以保持您的理智。

过时的信息(请勿这样做):

可以在一个步骤中完成多个孙子加载,但是这需要相当尴尬的逆转,然后才能在下一个节点向下之前备份图形(注意:这不适用于AsNoTracking()-您会遇到运行时错误):

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

因此,我将保留第一个选项(每个叶子实体深度模型包含一个)。


4
我想知道如何使用强类型的.include语句。答案就是用Select投影孩子!

1
我的等值“ co.Employees.Select(...)”显示“ Select”的语法错误,表示“'Employees'不包含'Select'的定义[或扩展方法]”。我包含了System.Data.Entity。我只想从联接表中获取单个列。
克里斯·沃尔什

1
我有一个父表两次引用相同的子表。使用旧的字符串include语法,很难预载正确的关系。这种方式更加具体。请记住要为强类型的include包括名称空间System.Data.Entity。
卡尔

1
使用.net core 2.1,我需要使用命名空间Microsoft.EntityFrameworkCore而不是System.Data.Entity
denvercoder9 '18


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.