EFCore SQLite中Linq的Take().Last()查询结果不符预期如何解决?
这个问题我之前在EF Core SQLite开发时也踩过坑,本质是EF Core对Last()方法的SQL转换逻辑在这种场景下出了问题,咱们一步步来拆解解决:
问题根源
SQLite本身没有原生的LAST()函数,EF Core处理Last()时,默认会把它转成ORDER BY [列名] DESC LIMIT 1的SQL语句。但当你在Take(20)之后调用Last()时,EF Core的查询翻译器并没有正确组合这两个操作——它直接忽略了前面的Take(20)限制,对整个数据集执行了反向排序取第一条的操作,这就导致你拿到的是全表最后一条,而非前20行的最后一条。
修复方案
有两种简单高效的替代写法,都能让EF Core生成正确的SQL:
方法一:反向排序后取第一条
既然Last()的本质是取排序序列的最后一项,那我们可以对Take(20)后的结果反向排序,再取第一条,就能得到前20行的最后一条数据:
// 原错误代码 // long tEnd = current.Last().Time; // 修改后代码 long tEnd = current.OrderByDescending(row => row.Id).First().Time;
EF Core会生成这样的SQL,确保先取前20行,再从中获取最后一条:
SELECT TOP(1) [t].[Time] FROM ( SELECT TOP(20) [d].[Id], [d].[Time] FROM [DataEntry] AS [d] ORDER BY [d].[Id] ) AS [t] ORDER BY [t].[Id] DESC
方法二:跳过前19条取第一条
因为你已经通过OrderBy(row => row.Id).Take(20)拿到了按Id升序的前20行,那前20行的最后一条就是第20条,直接跳过前19条取第一条即可:
long tEnd = current.Skip(19).First().Time;
这种写法生成的SQL更简洁高效:
SELECT [d].[Time] FROM [DataEntry] AS [d] ORDER BY [d].[Id] LIMIT 1 OFFSET 19
额外提示
尽量避免在EF Core查询中直接使用Last()/LastOrDefault(),尤其是搭配Take()/Skip()这类限制条件时——不同数据库的查询翻译器对这类组合的支持差异较大,用OrderByDescending().First()或Skip(n).First()的写法兼容性更强,逻辑也更直观。
内容的提问来源于stack exchange,提问作者fluter

