2013-09-23 37 views
2

我执行使用实体框架的服务器端分页,并有下面的代码LINQ到实体排序依据评估时的KeySelectors传递

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事件探查器)

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] 

其中,据我了解,查询表所有数据,然后对其进行处理的服务器。

所以,我有2个问题:

  1. 为什么查询变化?
  2. 我该如何修复它(能够改变keySelector并只查询必要的数据)?

回答

2

DbQuery<T>派生自IQueryable<T>IEnumerable<T>类。这两个类都提供了OrderBy方法,其中一个区别是:OrderByIEnumerable上获得Func<T1,T2>OrderBy,在IQueriable获得Expression<Func<T1,T2>>参数。当您将keyselector作为Func<T1,T2>对象传递给OrderBy方法时,您告诉编译器:嘿!请使用在IEnumerable上定义的OrderBy方法。换句话说你的DbQuery<T>对象被铸造到IEnumerable<T>而不是IQueriable<T>.这就是为什么所有的数据都被提取到客户端,并且进一步的操作是在内存中完成的。

为了解决这个问题,更改的keyselectorFunc<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; 
}