如何在 EF Core 中按条件获取最新条目?

Posted

技术标签:

【中文标题】如何在 EF Core 中按条件获取最新条目?【英文标题】:How do I get the most recent entry by condition in EF Core? 【发布时间】:2021-11-16 13:57:52 【问题描述】:

我有一个具有以下结构(和示例数据)的表:

Identifier UseDate PartId
a123 05/01/2000 237
a123 05/01/2000 4656
a123 01/01/2000 2134
a124 04/01/2000 5234
a124 01/01/2000 2890

我需要获取每个(非唯一)标识符的最新条目,但每个标识符最多一个。

似乎可以解决我的问题的 SQL 查询(MariaDB)如下:

SELECT a.Identifier, a.MaxDate, b.PartId, b.UseDate 
FROM
(SELECT Identifier, MAX(UseDate) AS MaxDate FROM MyTable GROUP BY Identifier) a
LEFT JOIN MyTable b ON a.Identifier = b.Identifier
WHERE a.MaxDate = b.UseDate GROUP BY a.Identifier;

但是我需要它来使用 C# 和 EF Core (Pomelo.EntitiFrameworkCore.mysql 5.0.3),我的尝试是:

var q1 = db.MyTable
            .GroupBy(t => t.Identifier)
            .Select(t => new  Identifier = t.Key, MaxDate = t.Max(x => x.UseDate) );

return new ObjectResult(db.MyTable
    .Join(
        q1,
        t1 => t1.Identifier,
        t2 => t2.Identifier,
        (t1, t2) => new  Identifier = t2.Identifier, PartId = t1.PartId, MaxDate = t1.MaxDate, UseDate = t1.UseDate )
    .Where(t => t.UseDate == q1.First(x => x.Identifier == t.Identifier).MaxDate)
    .GroupBy(t => t.Identifier)
    .ToList()
);

return new ObjectResult(db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(t => t.OrderByDescending(x => x.UseDate).FirstOrDefault())
    .ToList()
);

第一个抛出这个错误:

System.InvalidOperationException:“无法翻译给定的 'GroupBy' 模式。在 'GroupBy' 之前调用 'AsEnumerable' 以在客户端对其进行评估。”

第二个基本上产生相同的结果,只是抱怨 LINQ 表达式而不是 GroupBy。

我想避免使用原始 SQL,但如何正确(并希望有效地)实现它?

【问题讨论】:

您的目标是哪个 EFC 版本? 我正在使用 Pomelo.EntityFrameworkCore.MySql 5.0.3。 【参考方案1】:

在 LINQ 中编写此类查询的方法有很多,其中大多数都可以通过 EF Core 5/6+ 进行翻译。

为必要的分组和聚合定义子查询后,直接的方法是将其加入数据表,但不能使用 join 运算符 - 而是使用行限制相关子查询(SelectManyWhereTake),例如

var query = db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(t => new  Identifier = t.Key, MaxDate = t.Max(x => x.UseDate) )
    .SelectMany(g => db.MyTable
        .Where(t => t.Identifier == g.Identifier && t.UseDate == g.MaxDate)
        .Take(1));

如果排序字段对于每个其他键值都是唯一的(即在您的情况下,如果 UseDate 对于每个唯一的 Identifier 值都是唯一的),您可以直接使用 Join 运算符(因为需要限制),例如

var query = db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(t => new  Identifier = t.Key, MaxDate = t.Max(x => x.UseDate) );
    .Join(db.MyTable,
        g => new  g.Identifier, UseDate = g.MaxDate ,
        t => new  t.Identifier, t.UseDate ,
        (g, t) => t);

或直接将Max基于Where条件应用于数据表:

var query = db.MyTable
    .Where(t => t.UseDate == db.MyTable
        .Where(t2 => t2.Identifier == t.Identifier)
        .Max(t2 => t2.UseDate)
    );

最后,获得每组前 1 项的“标准”LINQ 方式。

对于 EF Core 6.0+:

var query = db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(g => g
        .OrderByDescending(t => t.UseDate)
        .First());

对于 EF Core 5.0,必须模拟查询内的分组结果集:

var query = db.MyTable
    .GroupBy(t => t.Identifier)
    .Select(g => db.MyTable
        .Where(t => t.Identifier == g.Key)
        .OrderByDescending(t => t.UseDate)
        .First());

【讨论】:

以上是关于如何在 EF Core 中按条件获取最新条目?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 EF Core 中仅包含相关表的最新记录

如何在 EF / EF Core 中的第二个表上实现具有某些条件的左连接?

如何在 .net core ef 上应用 thenInclude 条件?

如何在 C# EF Core 中使用 join 子句中的条件编写 SQL 命令

EF Core - 在数据库中按顺序插入记录,PK 上没有标识

ASP.Net Core 如何在 EF Core 和 Identity 中获取用户角色