Entity Framework无法转换Linq表达式

3
我有以下数据模型,我需要按以下条件对ResponseItem的列表进行分组:
  • 首先:ResponseItem.Group进行分组
  • 其次:ResponseItem.SubGroup进行分组,但只考虑最近的一项,也就是考虑ResponseItem.CreationDate

代码:

    public class ResponseItem
    {
        public string   Group        { get; set; }
        public string   SubGroup     { get; set; }
        public double   Value        { get; set; }
        public DateTime CreationDate { get; set; }
    }
    
    public class GroupedResponseItem
    {
        public string             Group { get; set; }
        public List<ResponseItem> Items { get; set; }
    }

方法是:
public List<GroupedResponseItem> GetGroupedData( IQueryable<ResponseItem> responseItems )
{
    return responseItems
        .OrderByDescending(i => i.CreationDate)
        .GroupBy(i => i.Group)
        .Select(grp => new GroupedResponseItem()
        {
            Group = grp.Key,
            Items = grp
                .GroupBy(i => new { i.SubGroup })
                .Select(grp => grp.First())
                .Select(i => new ResponseItem()
                {
                    SubGroup     = i.SubGroup,
                    CreationDate = i.CreationDate,
                    Value        = i.Value
                }).ToList()
         })
        .ToList();
}

但是我收到了一个错误消息:

'LINQ表达式“ProjectionBindingExpression: 0”无法翻译。请将查询重写为可翻译的形式,或通过插入对'AsEnumerable'、'AsAsyncEnumerable'、'ToList'或'ToToListAsync'的调用来明确地切换到客户端评估'

正如标题所述,我正在使用 .NET 6 上的 Entity Framework。
另一方面,如果我不考虑第二个分组,查询就可以正常工作:
public List<GroupedResponseItem> GetGroupedData(IQueryable<ResponseItem> responseItems)
{
    return responseItems
        .OrderByDescending(i => i.CreationDate)
        .GroupBy(i => i.Group)
        .Select(grp => new GroupedResponseItem()
        {
            Group = grp.Key,
            Items = grp
                .Select(i => new ResponseItem()
                {
                    SubGroup     = i.SubGroup,
                    CreationDate = i.CreationDate,
                    Value        = i.Value
                })
                .ToList()
        })
        .ToList();
}


尝试执行以下代码:.OrderByDescending(i => i.CreationDate).ToList() - Paul Sinnema
它可能在最后的选择中挣扎着使用了 .ToList()。通常,ToList 的意思是“现在就做”,而在您的查询中,您已经完成了一半的评估。 - Neil
如果我删除 ToList(),我会遇到相同的问题。 - user2382627
鉴于responseItems已经是一个ResponseItem对象序列,你为什么要在.Select中创建新实例呢?这完全是多余的。 - Dai
1个回答

4
罪魁祸首似乎是这里的次要投影(Select)。
.GroupBy(i => new { i.SubGroup })
.Select(grp => grp.First()) // <-- (1)
.Select(i => new ResponseItem() // <-- (2)
{
    SubGroup     = i.SubGroup,
    CreationDate = i.CreationDate,
    Value        = i.Value
})
.ToList()

虽然 EF Core 6.0 已经改进了对 GroupBy 的翻译,使得分组结果集上有了其他操作符的支持(除了键/聚合之外,这些都已经自然支持 SQL),但仍存在某些结构翻译存在限制或缺陷。特别是多个投影。

简单来说,在 GroupBy 后面的 Select 必须是最终的 LINQ 运算符。这有点令人沮丧,因为中间投影通常有助于翻译,并且经常用于解决 EF Core 的限制问题。但在这种情况下不行。

对于此查询,投影似乎是多余的,因为分组元素的类型与所投影的类型相同,因此可以将其简单地删除。

.GroupBy(i => new { i.SubGroup })
.Select(grp => grp.First()) // <-- final projection
.ToList()

所以这是其中一种解决方案/解决办法。如果您确实需要投影,因为您正在选择部分列或将其项目化为不同类型,则将其移至 GroupBy 之后的 Select 中:

.GroupBy(i => new { i.SubGroup })
.Select(grp => grp
    .Select(i => new ResponseItem()
     {
         SubGroup = i.SubGroup,
         CreationDate = i.CreationDate,
         Value = i.Value
     })
     .First()
) // <-- final projection
.ToList()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接