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

Entity Framework Core 使用导航属性替代Join查询指定日期类型站点关联线路

实现方案

前提确认

你需要先确保实体的导航属性配置正确(无需数据库存在物理外键,只需EF Core层面配置关系即可),核心导航属性如下:

  • Stop 包含 ICollection<Point> Points
  • Point 包含 Route Route
  • Route 包含 ServiceDetail ServiceDetail
  • ServiceDetail 包含 ServiceHeader ServiceHeader
  • ServiceHeader 包含 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();
}

原错误写法问题说明

  1. 不能在ThenInclude的lambda内直接嵌套Where,过滤包含的过滤条件需要跟在对应导航属性的Include/ThenInclude之后,是EF Core 5.0及以上版本才支持的功能
  2. 重复写Include路径的写法本身是合规的(因为需要加载同层级下的两个不同导航属性),但你原写法缺少闭合括号,语法错误
  3. Include是用于加载整个实体关联的场景,如果你只需要最终的Stop和Line实体,用投影写法更简洁高效,不需要写Include

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 19:45:03