迁移到.net core 3.1之后,EF OrderBy出现问题

时间:2019-12-15 17:26:28

标签: c# entity-framework-core linq-to-entities .net-core-3.1 ef-core-3.1

考虑以下代码:

_dbContext.Messages
    .GroupBy(m => new
        {
            MinId = m.SenderId <= m.RecipientId ? m.SenderId : m.RecipientId,
            MaxId = m.SenderId > m.RecipientId ? m.SenderId : m.RecipientId
        })
        .Select(gm => gm.OrderByDescending(m => m.SentAt).FirstOrDefault());

通过这种方式,无论用户是谁发送消息,我都按照用户ID将所有对话分组。然后,我按SentAt日期在组内排序消息,并从每个对话框中选择最后一条消息。 关键是该代码有效,并且更多地将其全部转换为纯T-Sql(我使用SQL Server Profiler进行检查)。但是后来我决定将我的项目从Core 2.1迁移到3.1,现在我明白了:

  

LINQ表达式'(GroupByShaperExpression:KeySelector:

new { 
    MinId = (CASE
        WHEN ((m.SenderId) <= (m.RecipientId)) THEN (m.SenderId)
        ELSE (m.RecipientId)
    END), 
    MaxId = (CASE
        WHEN ((m.SenderId) > (m.RecipientId)) THEN (m.SenderId)
        ELSE (m.RecipientId)
    END)
 }, 
ElementSelector:(EntityShaperExpression: 
    EntityType: Message
    ValueBufferExpression: 
        (ProjectionBindingExpression: EmptyProjectionMember)
    IsNullable: False
)
).OrderByDescending(m => m.SentAt)
  

无法翻译。可以使用以下形式重写查询:   进行翻译,或通过插入来明确切换到客户评估   调用AsEnumerable(),AsAsyncEnumerable(),ToList()或   ToListAsync()。有关详情,请参见https://go.microsoft.com/fwlink/?linkid=2101038   更多信息。

任何解决此问题的想法将不胜感激。

P.S。我知道我可以深入研究T-SQL并为其编写存储过程,但是我仍在寻找一种使用Linq to Entity来实现它的方法。

1 个答案:

答案 0 :(得分:2)

不幸的是,当前EF Core 3.0 / 3.1仅支持GroupBy的服务器翻译,并带有键/聚集的投影(类似于SQL)。

这是不能接受的,因为尽管EF6也没有客户端评估,但它能够成功翻译此类查询。

GroupBy翻译问题得到解决之前,解决方法是将GroupBy替换为2个相关的子查询-第一个仅包含分组关键字,第二个包含group元素。

在您的情况下,将是这样的:

var source = _dbContext.Messages
    .Select(m => new
    {
        Key = new
        {
            MinId = m.SenderId <= m.RecipientId ? m.SenderId : m.RecipientId,
            MaxId = m.SenderId > m.RecipientId ? m.SenderId : m.RecipientId
        },
        Message = m
    });

var query = source.Select(e => e.Key).Distinct()
    .SelectMany(key => source
        .Where(e => e.Key.MinId == key.MinId && e.Key.MaxId == key.MaxId)
        .Select(e => e.Message)
        .OrderByDescending(m => m.SentAt)
        .Take(1));