Entity Framework Core 使用导航属性替代Join查询指定日期类型站点关联线路
实现方案
前提确认
你需要先确保实体的导航属性配置正确(无需数据库存在物理外键,只需EF Core层面配置关系即可),核心导航属性如下:
Stop包含ICollection<Point> PointsPoint包含Route RouteRoute包含ServiceDetail ServiceDetailServiceDetail包含ServiceHeader ServiceHeaderServiceHeader包含ICollection<ServiceHeaderDay> ServiceHeaderDays、Line Line
推荐写法(投影查询,性能最优,无需手动Include/Join)
这种写法EF Core会自动根据导航属性生成等效的JOIN语句,不需要手动处理关联,也不需要加载多余的中间表字段,性能和你原有的手动JOIN写法完全一致:
var stops = await context.Stop // 过滤:只保留存在符合DayType条件关联记录的站点 .Where(s => s.Points .Any(p => p.Route.ServiceDetail.ServiceHeader.ServiceHeaderDays .Any(shd => shd.DayType == "L"))) // 直接投影出需要的站点和对应线路列表 .Select(s => new { StopEntity = s, RelatedLines = s.Points .Select(p => p.Route.ServiceDetail.ServiceHeader) .Where(sh => sh.ServiceHeaderDays.Any(shd => shd.DayType == "L")) .Select(sh => sh.Line) .Distinct() .ToList() }) .ToListAsync(); // 直接给站点的Lines属性赋值,无需额外分组 foreach (var item in stops) { item.StopEntity.Lines = item.RelatedLines; } // 最终结果集合 var result = stops.Select(x => x.StopEntity).ToList();
如果你确实需要使用Include的写法(EF Core 5.0+支持过滤包含)
仅在你需要用到Point、Route等中间实体的场景下使用,这种写法会加载所有中间表的字段,性能略低于投影写法:
var stops = await context.Stop // 第一条Include路径:加载到ServiceHeader的过滤后DayType记录 .Include(s => s.Points) .ThenInclude(p => p.Route) .ThenInclude(r => r.ServiceDetail) .ThenInclude(sd => sd.ServiceHeader) .ThenInclude(sh => sh.ServiceHeaderDays.Where(shd => shd.DayType == "L")) // 第二条Include路径:加载到ServiceHeader关联的Line实体 .Include(s => s.Points) .ThenInclude(p => p.Route) .ThenInclude(r => r.ServiceDetail) .ThenInclude(sd => sd.ServiceHeader) .ThenInclude(sh => sh.Line) // 过滤符合条件的站点 .Where(s => s.Points .Any(p => p.Route.ServiceDetail.ServiceHeader.ServiceHeaderDays .Any(shd => shd.DayType == "L"))) .ToListAsync(); // 处理去重得到线路列表 foreach (var stop in stops) { stop.Lines = stop.Points .Select(p => p.Route.ServiceDetail.ServiceHeader) .Where(sh => sh.ServiceHeaderDays.Any()) .Select(sh => sh.Line) .Distinct() .ToList(); }
原错误写法问题说明
- 不能在
ThenInclude的lambda内直接嵌套Where,过滤包含的过滤条件需要跟在对应导航属性的Include/ThenInclude之后,是EF Core 5.0及以上版本才支持的功能 - 重复写Include路径的写法本身是合规的(因为需要加载同层级下的两个不同导航属性),但你原写法缺少闭合括号,语法错误
- Include是用于加载整个实体关联的场景,如果你只需要最终的Stop和Line实体,用投影写法更简洁高效,不需要写Include
内容的提问来源于stack exchange,提问作者JSB
相关产品推荐
相关产品推荐

