我正在尝试优化我的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()进行本地评估,并返回带有过滤工具列表且未中断工具的雇员列表吗?谢谢。
答案 0 :(得分:2)
很快,这是不可能的(而且我认为永远不可能)。
如果您真的想控制确切的服务器调用,EF Core根本不适合您。尽管EF Core在某些LINQ查询转换方面仍然存在问题,从而导致N + 1查询或客户评估,但这是有意为之的:与EF6不同,EF6使用单个巨大的联合SQL查询来生成结果,而EF Core使用一个SQL查询来生成结果。主要结果集 plus 每个相关结果集每个一个SQL查询。
在How Queries Work EF Core文档部分中对此进行了解释:
- LINQ查询由Entity Framework Core处理,以构建表示形式,供数据库提供者处理
- 结果被缓存,因此无需在每次执行查询时都进行此处理
- 结果被传递给数据库提供者
- 数据库提供程序标识可以在数据库中评估查询的哪些部分
- 查询的这些部分被翻译成特定于数据库的查询语言(例如,关系数据库的SQL)
- 将一个或多个查询发送到数据库并返回结果集(结果是数据库中的值,而不是实体实例)
注意最后一个项目符号中的更多。
在您的情况下,您有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
由于评论,我更改了先前的答案,这是错误的。