You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级EF Core 8后LINQ查询执行极慢问题排查

EF Core 8关联查询性能骤降问题分析与解决

问题场景

原LINQ查询在EF Core 6/.NET 6环境下执行极快,但升级到.NET 8+EF Core 8后,执行耗时飙升至17秒。然而生成的底层SQL在SSMS中执行仅需不到1秒。

原查询代码:

return _db.LocationPolygons
    .Include(x => x.LocationPoints.OrderBy(y => y.SortOrder))
    .Where(x => x.LocationId == locationId && x.DeletedDateTime == null)
    .ToList();

生成的SQL:

DECLARE @__locationId_0 int = 11967;

SELECT [l].[LocationPolygonId], [l].[CentroidLat], [l].[CentroidLong], [l]. 
    [DeletedDateTime], [l].[Description], [l].[Editable], [l].[LocationId], [l0]. 
    [LocationPointId], [l0].[Latitude], [l0].[LocationPolygonId], [l0].[Longitude], [l0]. 
    [SortOrder]
FROM [LocationPolygon] AS [l]
LEFT JOIN [LocationPoint] AS [l0] ON [l].[LocationPolygonId] = [l0].[LocationPolygonId]
WHERE [l].[LocationId] = @__locationId_0 AND [l].[DeletedDateTime] IS NULL
ORDER BY [l].[LocationPolygonId], [l0].[SortOrder]

环境信息:

  • 两张表均使用int类型主键
  • LocationPoint表有LocationPolygonId + SortOrder联合索引
  • LocationPolygon表有LocationId + DeletedDateTime联合索引
  • LocationPoint与LocationPolygon间外键约束配置正确
  • 查询返回1条LocationPolygon记录,关联44000条LocationPoints子实体

尝试过.AsSplitQuery()但性能提升不明显;手动改写为DTO投影后,查询耗时降至1秒内。

手动DTO查询代码:

var results = from polygons in _db.LocationPolygons
              where polygons.LocationId == locationId &&
              polygons.DeletedDateTime == null
              select new LocationPolygonDto
              {
                  LocationPolygonId = polygons.LocationPolygonId,
                  CentroidLat = polygons.CentroidLat,
                  CentroidLong = polygons.CentroidLong,
                  Description = polygons.Description,
                  LocationId = locationId,
                  Editable = polygons.Editable,
                  LocationPoints = _db.LocationPoints.Where(x => x.LocationPolygonId == polygons.LocationPolygonId).Select(x => new PointDto { Latitude = x.Latitude, Longitude = x.Longitude }).ToList()
              };

return results.ToList();

核心原因分析

  1. 实体跟踪与实例化开销:
    Include方式会创建完整的实体对象,EF Core需要为每个实体维护状态跟踪(如变更检测),同时要处理导航属性的关联关系(将44000个子实体绑定到主实体上)。而DTO投影只生成轻量级的DTO对象,无需状态跟踪,内存开销和处理逻辑大幅减少。

  2. Join结果的重复数据合并:
    生成的Join SQL会返回44000行包含重复主实体数据的结果集,EF Core需要在内存中把这些重复的主实体合并成单个实例,同时逐一关联对应的子实体。这个合并逻辑在EF Core 8中存在性能退化,相比EF Core 6的处理效率明显降低。

  3. 导航属性排序的额外处理:
    虽然SQL中已经按SortOrder排序,但EF Core在处理带排序的导航属性时,可能在内存中再次执行排序或额外的集合处理逻辑,进一步增加了开销。

解决方案

1. 优先使用DTO投影(已验证有效)

直接在查询中构造DTO是最优方案,既避免了EF的实体跟踪开销,又减少了不必要的对象实例化和关系维护逻辑,尤其适合子实体数量庞大的场景。

2. 关闭实体跟踪(若需返回实体)

如果必须返回实体而非DTO,可添加.AsNoTracking()关闭状态跟踪,减少内存开销:

return _db.LocationPolygons
    .Include(x => x.LocationPoints.OrderBy(y => y.SortOrder))
    .Where(x => x.LocationId == locationId && x.DeletedDateTime == null)
    .AsNoTracking()
    .ToList();

3. 升级EF Core补丁版本

检查是否为EF Core 8的已知性能问题,尝试升级到最新的EF Core 8补丁版本,部分性能退化问题可能已被官方修复。

内容的提问来源于stack exchange,提问作者Aaron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 06:37:03