我正在尝试构建一个将对查询进行分页的扩展方法。但为了避免异常:System.NotSupportedException 未被用户代码处理 Message=方法“Skip”仅支持 LINQ to Entities 中的排序输入。必须在方法“Skip”之前调用方法“OrderBy”。
我想检查 OrderBy 是否被应用,如果不只是返回查询......像这样的东西:
public static IQueryable<T> Paginate<T>(this IQueryable<T> query, int page, int pageSize = 50, int total = -1)
{
// check if OrderBy was applied
// THIS DOES NOT WORK!!!
//try
//{
// var orderedQueryable = query as IOrderedQueryable<T>;
//}
//catch (Exception)
//{
// // if the cast throws OrderBy was not applied <-- DOES NOT WORK!!!
// return query;
//}
page = (page < 1) ? 1 : page;
var limit = (pageSize <= 0 || (total >= 0 && pageSize > total)) ? 50 : pageSize;
var skip = (page - 1)*limit;
return query.Skip(skip).Take(limit);
}
为了让事情更有趣,我使用 Mycrosoft 的动态表达式 API(又名动态 LINQ),所以我的调用代码看起来像
return query
.OrderBy("Customer.LastName DESC, Customer.FirstName")
.Paginate(1,25, 2345)
.ToArray();
或者我可以使用像这样的强类型表达式来调用它
return query
.OrderByDescending(c=>c.LastName)
.ThenBy(c=>c.FirstName)
.Paginate(1,25,2345)
.ToArray();
这种检查可以吗?我厌倦了使用in 方法签名,但是当您使用 OrderBy(string expression) 时IOrderableQueryable<T>
Dynamic Linq 不会返回,因此扩展将不适用...IOrderableQueryable
更新
使用建议(@GertArnold 指出)唯一可行的解决方案是检查表达式树。但是,我没有使用整个解决方案,而是ExpressionVisitor
通过要求必须在OrderBy
or之后调用 Paginate 方法来简化我的解决方案OrderByDescending
。这允许我只检查表达式树中的当前节点,而不是搜索整个树。所以这就是我所做的:
// snip....class level
private static readonly string[] PaginationPrerequisiteMehods = new[] { "OrderBy", "OrderByDescending" };
// snip Paginate method
public static IQueryable<T> Paginate<T>(this IQueryable<T> query, int page, int pageSize = 50, int total = -1)
{
// require that either OrderBy or OrderByDescending was applied just before calling Paginate....
if (query.Expression.NodeType != ExpressionType.Call)
{
//TODO: logging -> "You have to apply OrderBy() or OrderByDescending() just before calling Paginate()"
return query;
}
var methodName = ((MethodCallExpression) query.Expression).Method.Name;
if (!Array.Exists(PaginationPrerequisiteMehods, s => s.Equals(methodName, StringComparison.InvariantCulture)))
{
//TODO: logging -> "You have to apply OrderBy() or OrderByDescending() just before calling Paginate()"
return query;
}
page = (page < 1) ? 1 : page;
var limit = (pageSize <= 0 || (total >= 0 && pageSize > total)) ? 50 : pageSize;
var skip = (page - 1)*limit;
return query.Skip(skip).Take(limit);
}