【发布时间】:2020-01-17 10:42:10
【问题描述】:
我有一个 ASP.NET Core Web API,它使用 Entity Framework Core(版本 2.0.2)返回一个名为 PhotoAlbum 的数据模型的分页列表。为此,它会建立一个IQueryable<PhotoAlbum>,如下所示:
var query = _context.PhotoAlbums
.Include(album => album.SpotlightPhotoView)
.ApplySecurity(user)
.ApplyFilter(filter)
.Sort(sortInfo);
其中ApplySecurity、ApplyFilter 和Sort 是我自己的扩展,它们分别应用了两个Where 过滤器和一个OrderBy 过滤器。最后,代码使用Skip 和Take 返回匹配数据的特定子集。
我很想在我的日志中看到以下警告:
无法翻译 LINQ 表达式“Take(__p_2)”,将在本地计算。
无法翻译 LINQ 表达式“Skip(__p_1)”,将在本地计算。
无法翻译 LINQ 表达式“where [album].Featured”,将在本地进行评估。
无法翻译 LINQ 表达式“where (([album].Complete OrElse False) OrElse False)”,将在本地进行计算。
阅读了这些警告后,我现在知道 EF Core 中的某些 Linq 方法无法转换为 SQL。其中包括聚合函数,如 Sum 和 Count 以及 Skip 和 Take。
所以我的第一个问题是:如果您想实现分页查询,推荐的解决方案是什么?存储过程是选项吗?
我的第二个问题是:为什么我会在 Where 子句周围出现警告?
为了详细说明第二个问题,应用[album].Featured 过滤器的代码如下所示...
query = query.Where(album => album.Featured);
应用[album].Complete 过滤器的代码如下所示...
query = query
.Where(album =>
album.Complete || filter.IncludeIncomplete
&& album.Published || filter.IncludeUnpublished);
...其中filter 是一个简单模型,其中包含一组定义如何过滤的布尔属性。
这是根据我的日志执行的实际 SQL(为了便于阅读,从 SELECT 中删除了几列):
SELECT [album].[AlbumID], [album].[AlbumDate], [album.SpotlightPhotoView].[PhotoViewID]
FROM [PhotoAlbum] AS [album]
LEFT JOIN [PhotoView] AS [album.SpotlightPhotoView] ON [album].[SpotlightPhotoViewID] = [album.SpotlightPhotoView].[PhotoViewID]
WHERE ([album].[Complete] = 1) AND ([album].[Featured] = 1)
ORDER BY [album].[AlbumDate] DESC
尽管有警告,它似乎已经很好地应用了 Featured 和 Complete 过滤器。
【问题讨论】:
-
您使用的是哪个 EF Core 版本?早期的 EF Core 版本没有实现每个 LINQ 运算符并使用本地评估。 EF Core 3 禁用了此功能。您是否尝试过将
Skip()和Take()用于简单 LINQ 查询? -
ApplySecurity、ApplyFilter和Sort是做什么的?那些过滤器是什么?如果其中任何一个使用无法转换为 SQL 的表达式,则必须在本地评估整个查询。关于where [album].Featured的警告也很奇怪——这是本地计算的属性吗?无法在服务器上进行评估,因为服务器不知道Featured的公式是什么 -
顺便说一句,
These include aggregation functions like Sum and Count as well as Skip and Take.不适用于聚合,因为 EF Core 2.1 是最新的 LTS(长期支持)版本。您使用的是较早的、可能不受支持的版本吗? -
顺便说一句,我无法在 LinqPad 中重现 EF 2.2.6 或 EF Core 3 Preview 8 的任何问题。在这两种情况下,查询
Posts.OrderBy(p=>p.PostId).Skip(100).Take(100)都会导致ORDER BY [p].[PostId] OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY -
所有 LINQ 函数都可以转换为 SQL。这是他们的主要目的之一,其中包括
Skip和Take。将代码贴在ApplySecurity、ApplyFilter和Sort后面。您可能在那里做了一些奇怪的事情导致了这个问题。
标签: c# linq entity-framework-core