我正在使用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
并仅查询必要的数据)?答案 0 :(得分:2)
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;
}