是否有任何已知的解决方案可以在使用 EF Core 的延迟加载功能时避免 N+1 查询?



使用EF Core5和Microsoft.EntityFrameworkCore.Proxies,当涉及到嵌套对象时,我看到了很多N+1查询。

edit要明确的是,正如公认的答案所说,这个问题甚至与N+1查询无关。而且,正如我在评论中提到的,GraphQL阻止开发人员使用Include()来塑造查询,因为查询的形状留给了用户。传统的GraphQL优化(如Apollo实现的(使用查询聚合和延迟加载来解决我在这个问题中描述的问题

例如,考虑对GET /machines/的API调用,这意味着返回一个机器列表。每台机器有Parts,每台PartSettings。API调用旨在在同一响应中返回所有这些嵌套对象。

为了简单起见,这里是控制器端点:

public async Task<IActionResult> List() 
{
MachineProfile[] machineProfiles = await Db<MachineProfile>().Select(x => x).ToArrayAsync();
Dictionary<string, object>[] res = machineProfiles.Select(ToJson).ToArray();
return ApiResult(res);
}
public Dictionary<string, object> ToJson(MachineProfile m) 
{
return new Dictionary<string, object>() {
[nameof(m.Id)] = m.Id,
[nameof(m.Parts)] = m.Parts.Select(ToJson).ToArray(),
};
}
public Dictionary<string, object> ToJson(MachinePart m) 
{
return new Dictionary<string, object>() {
[nameof(m.Id)] = m.Id,
[nameof(m.Settings)] = m.Settings.Select(ToJson).ToArray(),
};
}
public Dictionary<string, object> ToJson(MachineSetting m) 
{
return new Dictionary<string, object>() {
[nameof(m.Id)] = m.Id,
};
}

下面是JSON结果的样子。

执行的SQL查询数为:

  • MachineProfiles1x
  • 2个用于MachineParts(每台机器一个(
  • 11x用于MachineSettings(每个零件一个,即使同一零件出现两次(

这是一个非常优化的N+1查询的经典示例。

是否存在任何包或众所周知的解决方案来避免这种情况?

对于这个问题,存在GraphQL解决方案,它们将线程块注入查询树遍历中。这与Microsoft.EntityFrameworkCore.Proxies的工作方式类似,只是它延迟并批处理查询以解决N+1问题。我想知道是否值得将这些代码移植到EF Core,或者是否有现有的解决方案。

--

Edit1我尝试使用GraphQL包,因为我计划无论如何都将服务器构建为GraphQL。我确实看到它实际上提高了查询计数。

例如,如果我使用这个查询:

{ getMachineProfiles { id parts { id settings { id } } } }

它产生了相同的JSON结果,但通过缓存调用内部的查询,略微提高了性能。换句话说,同一setting在同一响应中出现两次将不会引起第二次数据库调用。

但是11个数据库查询仍然远远多于"1";应该";使用(此API调用的优化版本只需要3个数据库查询(。

看起来Facebook的DataLoader有一个.NET端口,它实现了我在OP(查询聚合(中描述的解决方案。

该解决方案依赖于GraphQL包,但可以通过在后台使用GraphQL和预编译查询来优化标准RESTful端点。

最新更新