【问题标题】:Group by column and get first record of group using Linq按列分组并使用 Linq 获取组的第一条记录
【发布时间】:2020-06-18 20:39:25
【问题描述】:

我正在尝试使用 Linq 查询数据库,以仅从 SmsMessages 表中获取患者从每个患者发送的最后一条消息。我正在尝试按患者 ID 对消息进行分组,按日期对分组进行排序,然后返回该组的第一条记录,如下所示:

   var sms = await _dataContext.SmsMessages
                .GroupBy(message => message.PatientId)
                .Select(group => group.OrderByDescending(message => message.CreatedOn).FirstOrDefault())
                .ToListAsync();

类似问题的几个答案,for example,建议对上述内容进行变体,但在执行查询时收到以下错误: 错误:

失败:Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware1 执行请求时发生未处理的异常。 System.InvalidOperationException: LINQ 表达式'(GroupByShaperExpression: KeySelector: (s.PatientId), 元素选择器:(实体形状表达式: 实体类型:SmsMessage 值缓冲区表达式: (ProjectionBindingExpression:EmptyProjectionMember) IsNullable: 假 ) ) .OrderByDescending(message => message.CreatedOn)' 无法翻译。要么以可翻译的形式重写查询,要么切换到客户评估。

【问题讨论】:

  • 分组前按日期降序排列。分组不会改变顺序。所以 then .Select(group => group.First()) 会得到每个病人的最后一条消息。
  • 你用的是什么EF? EF 6/EF Core 2.0/EF Core 2.1/EF Core 3.0/EF Core 3.1?
  • 我使用的是 EF 核心 3.1

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


【解决方案1】:

在我的情况下,我无法在客户端进行分组,因为当只需要最后一行时,数据太重而无法将所有行下载到客户端。在花了一些时间尝试不同的 ef core 和 LINQ 表达式之后,我发现的唯一方法是使用使用临时表的 Raw SQL 查询。 SQL 查询如下所示:

SELECT message.* FROM message JOIN 
(SELECT PatientId, MAX(message.CreatedOn) AS CreatedOn 
FROM message GROUP BY message.PatientId) temp 
ON (message.PatientId = temp.PatientId and message.CreatedOn = temp.CreatedOn);

那么应该使用方法FromSqlRaw 来使用实体框架调用这个查询。

var sms = _dataContext.SmsMessages.FromSqlRaw(
    "SELECT message.* FROM message JOIN (SELECT PatientId, MAX(message.CreatedOn) AS CreatedOn FROM message GROUP BY message.PatientId) temp ON (message.PatientId = temp.PatientId and message.CreatedOn = temp.CreatedOn);").
    ToList();

【讨论】:

    【解决方案2】:

    此问题与Client vs. Server Evaluation有关。

    在进行GroupBy之前,需要在_dataContext.SmsMessages中添加ToList()操作,将服务端操作转换到客户端。

    否则,在服务器端,sql server 无法识别客户端方法。

    只需将您的代码更改如下:

     var sms = await _dataContext.SmsMessages.ToList()
                    .GroupBy(message => message.PatientId)
                    .Select(group => group.OrderByDescending(message => message.CreatedOn).FirstOrDefault())
                    .ToListAsync();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-17
      • 2014-08-29
      • 1970-01-01
      相关资源
      最近更新 更多