在 LINQ Where(x => f(x) > y) 和 OrderBy(f) 与 SQL 编译中使用相同的委托
using same delegate in LINQ Where(x => f(x) > y) and OrderBy(f) with SQL compilation
我正在尝试为 EFCore 编写一个相对简单但通用的分页函数,但我无法找到正确的函数类型来确保我的排序键选择器被转换为 SQL Where
和 OrderBy
子句。
public List<TItem> GetPage<TItem>(IQueryable<TItem> items, TFunc keyExtractor, int? itemsAfter = default)
{
if (itemsAfter != default)
{
items = items.Where(item => keyExtractor(item) > itemsAfter);
}
var materialized = items.OrderBy(keyExtractor).Take(pageSize).ToList();
// ... stuff to trim page size ...
}
- 当
TFunc
为 Expression<Func<TItem, int>>
时,它在 OrderBy
中得到了正确处理,但 Where
子句必须更改为 keyExtractor.Compile().Invoke(item) > itemsAfter
且未被翻译,给出我“Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式 'where (Convert(__Compile_1.Invoke([t]), Nullable`1) > __itemsAfter_2)' 无法翻译,将在本地求值。”
- 当
TFunc
为 Func<TItem, int>
时,它由 Where
子句正确处理,但 OrderBy
使用 IEnumerable.OrderBy
而不是 IQueryable.OrderBy
.这意味着 order 和 take 是在本地完成的——这对顶级对象本身来说很好,但是子属性不考虑 Take
并提取大量数据。
是否有更好的方法将其中一种转换为另一种,以便翻译查询?或者有没有我可以接受的第三种类型可以很容易地制成这两种类型?我的 keyExtractor 参数都比较简单,例如item => item.id
。由于我无法控制的原因,我目前只能使用 EFCore 2.1
对于Expression<Func<TItem, int>>
,您需要自己构建大于表达式。沿着这条线:
public List<TItem> GetPage<TItem>(IQueryable<TItem> items, Expression<Func<TItem, int>> keyExtractor, int? itemsAfter = default)
{
if (itemsAfter != default)
{
var greaterThan = Expression.GreaterThan(keyExtractor.Body, Expression.Constant(itemsAfter));
var filter = Expression.Lambda<Func<TItem, bool>>(greaterThan, keyExtractor.Parameters);
items = items.Where(filter);
}
var materialized = items.OrderBy(keyExtractor).Take(pageSize).ToList();
// ... stuff to trim page size ...
}
我正在尝试为 EFCore 编写一个相对简单但通用的分页函数,但我无法找到正确的函数类型来确保我的排序键选择器被转换为 SQL Where
和 OrderBy
子句。
public List<TItem> GetPage<TItem>(IQueryable<TItem> items, TFunc keyExtractor, int? itemsAfter = default)
{
if (itemsAfter != default)
{
items = items.Where(item => keyExtractor(item) > itemsAfter);
}
var materialized = items.OrderBy(keyExtractor).Take(pageSize).ToList();
// ... stuff to trim page size ...
}
- 当
TFunc
为Expression<Func<TItem, int>>
时,它在OrderBy
中得到了正确处理,但Where
子句必须更改为keyExtractor.Compile().Invoke(item) > itemsAfter
且未被翻译,给出我“Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式 'where (Convert(__Compile_1.Invoke([t]), Nullable`1) > __itemsAfter_2)' 无法翻译,将在本地求值。” - 当
TFunc
为Func<TItem, int>
时,它由Where
子句正确处理,但OrderBy
使用IEnumerable.OrderBy
而不是IQueryable.OrderBy
.这意味着 order 和 take 是在本地完成的——这对顶级对象本身来说很好,但是子属性不考虑Take
并提取大量数据。
是否有更好的方法将其中一种转换为另一种,以便翻译查询?或者有没有我可以接受的第三种类型可以很容易地制成这两种类型?我的 keyExtractor 参数都比较简单,例如item => item.id
。由于我无法控制的原因,我目前只能使用 EFCore 2.1
对于Expression<Func<TItem, int>>
,您需要自己构建大于表达式。沿着这条线:
public List<TItem> GetPage<TItem>(IQueryable<TItem> items, Expression<Func<TItem, int>> keyExtractor, int? itemsAfter = default)
{
if (itemsAfter != default)
{
var greaterThan = Expression.GreaterThan(keyExtractor.Body, Expression.Constant(itemsAfter));
var filter = Expression.Lambda<Func<TItem, bool>>(greaterThan, keyExtractor.Parameters);
items = items.Where(filter);
}
var materialized = items.OrderBy(keyExtractor).Take(pageSize).ToList();
// ... stuff to trim page size ...
}