如何按孙 属性 对 LINQ / Entity Framework 查询进行排序

How to sort LINQ / Entity Framework query by grandchild property

我 运行 遇到 LINQ 查询问题。我有以下数据结构(Entity Framework 类 映射到 SQL 服务器数据库):

Event 有一个 EventBlocks 属性,其中有 0 个或多个 EventBlock 对象作为子对象。 EventBlock 有一个 Details 属性,其中有 0 个或多个 EventBlockDetail 对象作为子对象。 EventBlockDetail 有一个 DetailDate 和一个 StartTime。

我需要在网站上显示接下来 3 场即将举行的活动。排序顺序由事件的最早开始日期和时间决定,存储在 Event -> EventBlock -> EventBlockDetail 中。如果接下来的 2 或 3 个即将到来的 EventBlockDetails 恰好属于同一个事件,则该事件应该只显示一次,而另一个事件应该出现在前 3 个中。

有没有什么方法可以通过单个 LINQ 查询来完成此操作,这样我就不必在从数据库中获取所有事件数据后进行大量循环和比较?

为了说明我正在尝试做的事情,这里是一个虚构的查询,它显然不起作用,因为第一个 OrderBy 不知道要按哪个 属性 进行排序。

var result = this.Context.Events.Where(
    e => e.WebsiteId == websiteId && 
        e.EventBlocks.All(
            b => b.Details.All(
                d => d.DetailDate >= DateTime.Now
            )
        )
    ).OrderBy( 
        e => e.EventBlocks.OrderBy(
            b => eb.Details.Min(d => d.DetailDate)).ThenBy(
            b => eb.Details.Min(d => d.StartTime))
    ).Take(nrOfEvents).ToList();

您应该能够 select 通过按日期和时间对所有事件详细信息进行排序并取第一个来 select 事件和最早的日期和时间。然后您可以使用该详细信息来过滤和排序事件。

var result = Context.Events
    .Where(e => e.WebsiteId == websiteId)
    .Select(e => new 
    { 
        Event = e, 
        StartDetails = e.EventBlocks
            .SelectMany(b => b.Details)
            .OrderBy(d => d.DetailDate) 
            .ThenBy(d => d.StartTime)
            .FirstOrDefault()
    })
    .Where(x => x.StartDetails.DetailDate >= DateTime.Now)
    .OrderBy(x => x.StartDetails.DetailDate)
    .ThenBy(x => x.StartDetails.StartTime)
    .Take(nrOfEvents)
    .Select(x => x.Event)
    .ToList();

请注意,如果 DetailDate 的时间被截断,x => x.StartDetails.DetailDate >= DateTime.Now 过滤器可能会删除尚未开始的内容。在这种情况下,您可能实际上想要在 DateTime.Today 上进行过滤,但如果日期是今天,则如果时间早于当前时间也进行过滤。例如,如果详细信息的日期为 2019-09-25(推测存储时间为午夜),则与 2019-09-25 上午 8 点(如果这是当前日期时间)进行比较将失败,即使详细信息的开始时间是早上 8 点以后。

因此,假设 StartTimeTimespan 或基于 StartTime 是什么的类似内容,过滤器可能应该是以下内容。

.Where(x => x.StartDetails.DetailDate > DateTime.Today ||
            (x.StartDetails.DetailDate == DateTime.Today &&
            x.StartDetails.StartTime >= DateTime.Now.TimeOfDay))

如果日期和时间存储在一列而不是两列中,这将更有意义并且是一个更简单的查询。