Entity Framework Core:使用导航属性而不是连接

Posted

技术标签:

【中文标题】Entity Framework Core:使用导航属性而不是连接【英文标题】:Entity Framework Core: using navigation properties instead of joins 【发布时间】:2021-09-15 16:19:12 【问题描述】:

最近我将Navigation Properties 添加到没有任何FK 的EF Core 项目中,以便在尽可能多的查询中使用Include 而不是Join,但我发现无论是sintax几乎所有查询都不适合我的需求(需要拆分为多个查询),或者我遗漏了一些东西,所以我举个例子,我想知道,使用Navigation Properties 你会如何获取特定 dayType 的所有 Stops 及其 Line 列表?:

Stop
stopId
Point
pointId
stopId?
routeId
Route
routeId
serviceDetailId
ServiceDetail
serviceDetailId
serviceHeaderId
ServiceHeaderDay
serviceHeaderDayId
serviceHeaderId
dayType
ServiceHeader
serviceHeaderId
lineId
Line
lineId

使用Join 的当前工作查询,我想使用Include 进行翻译:

var query = (await context.Stop
    .Join(
        context.Point,
        (stop) => stop.stopId,
        (point) => point.stopId,
        (stop, point) => new  Stop = stop, Point = point )
    .Join(
        context.Route,
        (join) => join.Point.routeId,
        (route) => route.routeId,
        (join, route) => new  Stop = join.Stop, Route = route )
    .Join(
        context.ServiceDetail,
        (join) => join.Route.routeId,
        (serviceDetail) => serviceDetail.routeId,
        (join, serviceDetail) => new  Stop = join.Stop, ServiceDetail = serviceDetail )
    .Join(
        context.ServiceHeader,
        (join) => join.ServiceDetail.serviceHeaderId,
        (serviceHeader) => serviceHeader.serviceHeaderId,
        (join, serviceHeader) => new  Stop = join.Stop, ServiceHeader = serviceHeader )
    .Join(
        context.ServiceHeaderDay,
        (join) => join.ServiceHeader.serviceHeaderId,
        (serviceHeaderDay) => serviceHeaderDay.serviceHeaderId,
        (join, serviceHeaderDay) => new  Stop = join.Stop, ServiceHeader = join.ServiceHeader, ServiceHeaderDay = serviceHeaderDay )
    .Join(
        context.Line,
        (join) => join.ServiceHeader.lineId,
        (line) => line.lineId,
        (join, line) => new  Stop = join.Stop, ServiceHeaderDay = join.ServiceHeaderDay, Line = line )
    .Where(e => e.ServiceHeaderDay.DayType == "L")
    .Select(e => new  Stop = e.Stop, Line = e.Line )
    .Distinct();
    .ToListAsync())
    // The query ends here, this next step is just grouping by Stops and inserting each Line list into them.
    .GroupBy(e => e.Stop.stopId)
    .Select(e =>
            
        var stop = e.First().Stop;
        stop.Lines = e.Select(e => e.Line).ToList();
        return stop;
    )

使用Include 进行的失败尝试之一:

context.Stop
    .Include(e => e.Points)
    .ThenInclude(e => e.Route)
    .ThenInclude(e => e.ServiceDetail)
    .ThenInclude(e => e.ServiceHeader)
    .ThenInclude(e => e.ServiceHeaderDay
        Where(e => e.DayType = "L")
    // Now I need to Include Line from ServiceHeader, but this is a of type ServiceHeaderDay 
    // and I think you can't use anonymous objects to carry all the tables you just include 
    // so I found people repeating the includes like this:
    .Include(e => e.Point)
    .ThenInclude(e => e.Route)
    .ThenInclude(e => e.ServiceDetail)
    .ThenInclude(e => e.ServiceHeader)
    .ThenInclude(e => e.Line)
    // This doesn't seem to work, but also how would be the select to get the Stops with all 
    // the lines for each Stop here?
    .Select(e => ?)

【问题讨论】:

【参考方案1】:

如果我正确理解您的问题,您的查询可以大大简化。 Include 通常不是用于查询,而是用于加载相关数据进行修改。

var query = context.Stop
    .Where(s => s.Points.Any(p => p.Route.ServiceDetail.ServiceHeader.ServiceHeaderDay.DayType = 'L'))
    .Select(s => new 
    
        Stop = s,
        Lines = s.Points.Where(p => p.Route.ServiceDetail.ServiceHeader.ServiceHeaderDay.DayType = 'L')
            .Select(p => p.Route.ServiceDetail.ServiceHeader.Line)
            .ToList()
    );

【讨论】:

以上是关于Entity Framework Core:使用导航属性而不是连接的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core 性能优化

在 Entity Framework Core 中使用 [ComplexType]

在 Entity Framework Core 中使用 SQL 视图

使用 Entity Framework Core 更新相关数据

使用 Entity Framework Core 自动增加部分主键

如何使用 Entity Framework Core 模拟异步存储库