实体框架核心3模拟PARTITION BY

时间:2019-11-08 21:19:52

标签: c# entity-framework-core

我正在尝试使用EF Core 3检索每个组的最新记录,但是我想到的每个可能的LINQ查询都以subtree异常InvalidOperationException结尾。

根据在EF Core 2.2中有效的一些答案,该查询应该可以解决问题,但不能解决问题

Processing of the LINQ expression '...' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core

我尝试过的另一个选择是

from lfv in dbo.ListingFlagValues
group lfv by lfv.ListingId into groups
select groups.OrderByDescending(x => x.Timestamp).FirstOrDefault();

我要实现的行为类似于以下查询

db.ListingFlagValues.GroupBy(x => x.Listing)
  .Select(x => new { Group = x, MaxTimestamp = x.Max(y => y.Timestamp) })
  .SelectMany(x => x.Group.Select(y => new { y.ListingId, ValueId = y.NewFlagValueId, y.Timestamp, x.MaxTimestamp }))
  .Where(x => x.Timestamp == x.MaxTimestamp);

1 个答案:

答案 0 :(得分:2)

此处的键为“此异常消息中可能表示EF Core中的错误或限制” (将“可能表示为表示为”)表示“)。尽管EF Core 3.0改进了查询翻译,但它仍然不支持许多查询模式,尤其是对于GroupBy的结果。而且由于它还删除了客户端评估,因此由于无提示客户端评估而在2.x中工作的查询现在完全失败了。

另一方面,EF Core 3.0通过利用ROW_NUMBER OVER (PARTITION BY SQL构造改进了最后一项成组模式的翻译。但是,它不适用于GroupBy结果,因此您必须通过使用Distinct键查询和相关子查询值手动进行分组。

例如,以下LINQ查询

from listingId in db.ListingFlagValues.Select(x => x.ListingId).Distinct()
from lfv in db.ListingFlagValues
    .Where(x => x.ListingId == listingId)
    .OrderByDescending(e => e.Timestamp)
    .Take(1)
select lfv

使用以下SQL转换并成功执行

  SELECT [t1].[Id], [t1].[ListingId], [t1].[NewFlagValueId], [t1].[Timestamp]
  FROM (
      SELECT DISTINCT [l].[ListingId]
      FROM [ListingFlagValues] AS [l]
  ) AS [t]
  INNER JOIN (
      SELECT [t0].[Id], [t0].[ListingId], [t0].[NewFlagValueId], [t0].[Timestamp]
      FROM (
          SELECT [l0].[Id], [l0].[ListingId], [l0].[NewFlagValueId], [l0].[Timestamp], ROW_NUMBER() OVER(PARTITION BY [l0].[ListingId] ORDER BY [l0].[Timestamp] DESC) AS [row]
          FROM [ListingFlagValues] AS [l0]
      ) AS [t0]
      WHERE [t0].[row] <= 1
  ) AS [t1] ON [t].[ListingId] = [t1].[ListingId]