实体框架:左连接并列出结果

时间:2019-06-15 01:18:19

标签: entity-framework linq entity-framework-core

我正在尝试优化我的EF查询。我有一个名为Employee的实体。每个员工都有工具列表。最终,我试图获得一份员工名单以及他们没有损坏的工具。运行查询时,我看到对服务器进行了两次调用:一次是对雇员实体的调用,而一次是对工具列表的调用。同样,我正在尝试优化查询,因此只对服务器命中一次查询。我该怎么办?

我一直在研究LINQ的联接以及如何创建LEFT JOIN,但是查询仍未优化。

在这里的第一个代码块中,结果就是我想要的,但是-再次-服务器受到两次点击。

dash_table.DataTable
public class Employee
{
    public int EmployeeId { get; set; }
    public List<Tool> Tools { get; set; } = new List<Tool>();
    ...
}

public class Tool
{
    public int ToolId { get; set; }
    public bool IsBroken { get; set; } = false;

    public Employee Employee { get; set; }
    public int EmployeeId { get; set; }
    ...
}

第二个代码块伪造了我想要完成的工作。但是,正在客户端计算机上本地评估GroupBy(...)。

var x = (from e in db.Employees.Include(e => e.Tools)
         select new Employee()
         {
             EmployeeId = e.EmployeeId,
             Tools = e.Tools.Where(t => !t.IsBroken).ToList()
         }).ToList();

无论如何,我可以对服务器进行一次调用,也可以不对GroupBy()进行本地评估,并返回带有过滤工具列表且未中断工具的雇员列表吗?谢谢。

2 个答案:

答案 0 :(得分:2)

很快,这是不可能的(而且我认为永远不可能)。

如果您真的想控制确切的服务器调用,EF Core根本不适合您。尽管EF Core在某些LINQ查询转换方面仍然存在问题,从而导致N + 1查询或客户评估,但这是有意为之的:与EF6不同,EF6使用单个巨大的联合SQL查询来生成结果,而EF Core使用一个SQL查询来生成结果。主要结果集 plus 每个相关结果集每个一个SQL查询。

How Queries Work EF Core文档部分中对此进行了解释:

  
      
  1. LINQ查询由Entity Framework Core处理,以构建表示形式,供数据库提供者处理      
        
    • 结果被缓存,因此无需在每次执行查询时都进行此处理
    •   
  2.   
  3. 结果被传递给数据库提供者      
        
    • 数据库提供程序标识可以在数据库中评估查询的哪些部分
    •   
    • 查询的这些部分被翻译成特定于数据库的查询语言(例如,关系数据库的SQL)
    •   
    • 将一个或多个查询发送到数据库并返回结果集(结果是数据库中的值,而不是实体实例)
    •   
  4.   

注意最后一个项目符号中的更多

在您的情况下,您有1个主要结果集(Employee)+ 1个相关结果集(Tool),因此,预期的服务器查询为2(除非第一个查询返回空集)

答案 1 :(得分:1)

您可以使用此:

    var x = from e in _context.Employees
    select new
    {
        e,
        Tools = from tool in e.Tools where !tool.IsBroken select tool
    };
    var result = x.AsEnumerable().Select(y => y.e);

根据您的提供商,最终哪个将被翻译成如下所示的SQL查询:

    SELECT
`Project1`.`EmployeeId`, 
`Project1`.`Name`, 
`Project1`.`C1`, 
`Project1`.`ToolId`, 
`Project1`.`IsBroken`, 
`Project1`.`EmployeeId1`
FROM (SELECT
`Extent1`.`EmployeeId`, 
`Extent1`.`Name`, 
`Extent2`.`ToolId`, 
`Extent2`.`IsBroken`, 
`Extent2`.`EmployeeId` AS `EmployeeId1`, 
CASE WHEN (`Extent2`.`ToolId` IS NOT NULL) THEN (1)  ELSE (NULL) END AS `C1`
FROM `Employees` AS `Extent1` LEFT OUTER JOIN `Tools` AS `Extent2` ON (`Extent1`.`EmployeeId` = `Extent2`.`EmployeeId`) AND (`Extent2`.`IsBroken` != 1)) AS `Project1`
 ORDER BY 
`Project1`.`EmployeeId` ASC, 
`Project1`.`C1` ASC

由于评论,我更改了先前的答案,这是错误的。