【问题标题】:EF Core LINQ GROUPBY Then Select to get more than one properties of the entityEF Core LINQ GROUPBY 然后选择获取实体的多个属性
【发布时间】:2021-06-18 20:11:33
【问题描述】:

我有 2 个表 Outlet 和 Order 具有以下架构:

Outlet          Order
------    -------------------
Id              Id
Name            Name
                OrderCompletedTime
                NextOrderDueTime
                OutletIds

早些时候,当我想使用实体框架核心为每个出口获取 NextOrderDueTime 时,我做了:

return _dbAccessor.RequestContext.MyDbContext.Order
                .Where(i => i.OutletId == _dbAccessor.RequestContext.OutletId &&
                            !i.IsRemoved && i.NextOrderDueTime.HasValue)
                .GroupBy(i => i.OutletId)
                .Select(g => new { OutletId = g.Key, NextOrderDueTime = g.Min(x => x.NextOrderDueTime) })
                .ToDictionary(i => i.OutletId, i => i.NextOrderDueTime);

现在在 UI 上,我们需要将此到期时间作为链接,并希望用户根据订单 ID 导航到该订单详细信息页面

如何更改上述查询以同时返回 OrderId 以及时间?

我的想法:

  1. 将方法的返回类型从Dictionary<int, DateTimeOffset?>更改为Dictionary<int, Tuple<int,DateTimeOffset?>>

  2. 我尝试将 Linq 查询更改为:

return _dbAccessor.RequestContext.MyDbContext.Order
                .Where(i => i.OutletId == _dbAccessor.RequestContext.OutletId &&
                            !i.IsRemoved && i.NextOrderDueTime.HasValue)
                .GroupBy(i => i.OutletId)
                   .Select(g =>
                       new
                       {
                           OutletId = g.Key,
                           NextOrderDueTime = g.FirstOrDefault(x => x.NextOrderDueTime == g.Min(y => y.NextOrderDueTime)).NextOrderDueTime,
                           NextOrderId = g.FirstOrDefault(x => x.NextOrderDueTime == g.Min(y => y.NextOrderDueTime)).OrderId
                       })
                       .ToDictionary(i => i.OutletId, i => new Tuple<int, DateTimeOffset?>(i.NextOrderId, i.NextOrderDueTime));

但这会在运行时引发异常?

请帮助让我知道我在这里做错了什么。

【问题讨论】:

    标签: c# linq .net-core entity-framework-core


    【解决方案1】:

    您可以在返回时将整个订单连同 outletid 一起拿走:

    .Select(g => new { 
      OutletId = g.Key, 
      NextOrder = g.OrderBy(x => x.NextOrderDueTime).FirstOrDefault() 
    })
    

    您可以选择此选项以从订单中获取多个属性:

    .Select(g => new { 
      OutletId = g.Key, 
      NextOrder = g.OrderBy(x => x.NextOrderDueTime).FirstOrDefault() 
    })
    .Select(s => new {
      s.OutletId,
      NextOrderId = NextOrder.Id,
      NextOrder.NextOrderDueTime,
      NextOrderName = NextOrder.Name
    })
    

    等等。

    要欣赏的主要事情是,分组为您提供了一个具有键的对象,但它本身是具有该键的所有事物的列表,因此,如果您通过 DUeDat 之类的东西对列表进行排序并取第一件事,那么你有一个截止日期最短的整个对象,你可以从中获取各种东西

    【讨论】:

    • 使用上述 LINQ 时抛出了无效操作异常。异常消息说:' LINQ .... 无法翻译。以可以翻译的形式重写查询,或者通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。
    【解决方案2】:

    .GroupBy(...).Select(...).ToDictionary(...);自 EF Core 3.0 起无法转换为 SQL。

    由于 EF Core 3.0 中的重大更改。 https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes ,EF Core 3.0 将抛出异常,以确保您知道 Order 中的所有记录将在分组之前从数据库中获取并映射到 Dictionary。

    我的查询如下所示:

    return _dbAccessor.RequestContext.MyDbContext.Order
                    .Where(i => i.OutletId == _dbAccessor.RequestContext.OutletId &&
                                !i.IsRemoved && i.NextOrderDueTime.HasValue).AsEnumerable()
                    .GroupBy(i => i.OutletId)
                       .Select(g =>
                           new
                           {
                               OutletId = g.Key,
                               NextOrderDueTime = g.FirstOrDefault(x => x.NextOrderDueTime == g.Min(y => y.NextOrderDueTime)).NextOrderDueTime,
                               NextOrderId = g.FirstOrDefault(x => x.NextOrderDueTime == g.Min(y => y.NextOrderDueTime)).OrderId
                           })
                           .ToDictionary(i => i.OutletId, i => new Tuple<int, DateTimeOffset?>(i.NextOrderId, i.NextOrderDueTime));
    

    只需在 GroupBy 之前添加 AsEnumerable() 即可,如另一个答案所示:

     _dbAccessor.RequestContext.MyDbContext.Order
                    .Where(i => i.OutletId == _dbAccessor.RequestContext.OutletId &&
                                !i.IsRemoved && i.NextOrderDueTime.HasValue).AsEnumerable()
                    .GroupBy(i => i.OutletId)
                      .Select(g => new { 
      OutletId = g.Key, 
      NextOrder = g.OrderBy(x => x.NextOrderDueTime).FirstOrDefault() 
    })
    .Select(s => new {
      s.OutletId,
      NextOrderId = NextOrder.Id,
      NextOrder.NextOrderDueTime,
      NextOrderName = NextOrder.Name
    })`enter code here`;
    
    

    【讨论】:

      猜你喜欢
      • 2020-12-09
      • 2021-09-05
      • 1970-01-01
      • 2016-06-15
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多