我正在使用 Entity Framework 实现服务器端分页并具有以下代码
DbQuery<T> query = Context.Set<T>();
query = IncludeNavigationProperties(query, includedProperties);
var result = query.OrderBy(arg => arg.DatabaseId)
.Skip((pageNumber - 1)*pageSize)
.Take(pageSize).ToList();
这会生成一个仅查询必要数据的 SQL(使用 SQL Server Profiler 检查)
SELECT TOP (21)
[Extent1].[DatabaseId] AS [DatabaseId],
...[other props here]...
FROM ( SELECT [Extent1].[DatabaseId] AS [DatabaseId], ...[other props here]..., row_number() OVER (ORDER BY [Extent1].[DatabaseId] ASC) AS [row_number]
FROM [dbo].[Table] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 84
ORDER BY [Extent1].[DatabaseId] ASC
然后我决定在更多场景中重用此方法,并将 keySelector
作为外部变量传递:
DbQuery<T> query = Context.Set<T>();
query = IncludeNavigationProperties(query, includedProperties);
var result = query.OrderBy(keySelector)
.Skip((pageNumber - 1)*pageSize)
.Take(pageSize).ToList();
哪里
Func<T, int> keySelector = arg => arg.DatabaseId;
但是它突然生成以下 SQL 查询:
SELECT
[Extent1].[DatabaseId] AS [DatabaseId],
...[other props here]...
FROM [dbo].[Table] AS [Extent1]
据我了解,它会查询表中的所有数据,然后在服务器上对其进行处理。
所以,我有两个问题:
- 查询为何发生变化?
- 如何解决这个问题(能够改变
keySelector
并仅查询必要的数据)?
最佳答案
DbQuery<T>
源自 IQueryable<T>
和IEnumerable<T>
类。这两个类都提供OrderBy
方法有一个区别:OrderBy
上IEnumerable
得到 Func<T1,T2>
和OrderBy
上IQueriable
得到 Expression<Func<T1,T2>>
范围。当您经过 keyselector
作为Func<T1,T2>
反对OrderBy
方法中,你告诉编译器:嘿!请使用OrderBy
IEnumerable
上定义的方法. 换句话说,您的DbQuery<T>
对象被转换为 IEnumerable<T>
不是IQueriable<T>.
这就是为什么所有数据都被提取到客户端并进一步操作在内存中完成的原因。
要解决此问题,请更改 keyselector
的类型来自Func<T1,TKey>
至Expression <Func<T1,TKey>>
如:
public IQueriable<T> YourMethodName<T, TKey>(Expression<Func<T,TKey>> keyselector)
{
DbQuery<T> query = Context.Set<T>();
query = IncludeNavigationProperties(query, includedProperties);
var result = query.OrderBy(keySelector)
.Skip((pageNumber - 1)*pageSize)
.Take(pageSize).ToList();
return result;
}
关于c# - 当传递 keySelector 时,Linq to Entities OrderBy 会更早地评估查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18960761/