如何清除IQueryable中的OrderBy子句?
如何清除IQueryable中的OrderBy子句?
刚好碰到过类似的场景——当你手里拿到一个已经被排序过的IQueryable,但业务需求又需要它回到未排序状态时,直接修改原查询肯定不行,毕竟IQueryable本身是不可变的。不过我们可以通过解析它的表达式树,把排序相关的节点给剥离掉,完美解决这个问题。
实现思路
IQueryable的查询逻辑其实是存储在它的表达式树里的,我们只需要写一个表达式访问器,找到所有OrderBy/OrderByDescending/ThenBy/ThenByDescending的方法调用节点,然后返回这些调用之前的查询部分就可以了。
具体代码实现
首先定义一个继承自ExpressionVisitor的类,用来处理表达式树:
public class OrderByRemover : ExpressionVisitor { protected override Expression VisitMethodCall(MethodCallExpression node) { // 判断当前节点是不是LINQ的排序方法调用 if (node.Method.DeclaringType == typeof(Queryable) && (node.Method.Name == "OrderBy" || node.Method.Name == "OrderByDescending" || node.Method.Name == "ThenBy" || node.Method.Name == "ThenByDescending")) { // 返回排序方法的第一个参数,也就是排序操作之前的原始查询 return Visit(node.Arguments[0]); } return base.VisitMethodCall(node); } }
然后封装成扩展方法,方便直接调用:
public static class QueryableExtensions { public static IQueryable<T> RemoveOrders<T>(this IQueryable<T> query) { var orderRemover = new OrderByRemover(); // 处理原查询的表达式树 var newExpression = orderRemover.Visit(query.Expression); // 根据处理后的表达式树创建新的IQueryable return query.Provider.CreateQuery<T>(newExpression); } }
在你的代码中使用
现在就可以在GetUnorderedQuery里直接调用这个扩展方法了:
public IQueryable<SomeType> GetUnorderedQuery() { var query = GetOrderedQuery(); // 移除所有排序子句 var unorderedQuery = query.RemoveOrders(); return unorderedQuery; }
补充说明
- 这个方法会递归处理所有的排序节点,不管你链式调用了多少个
ThenBy,都会被一并移除; - 如果原查询本身没有排序子句,调用这个方法也不会有任何问题,会直接返回原查询;
- 这个方案适用于EF Core、LINQ to SQL这类基于表达式树的查询提供器,大部分业务场景下都能正常工作。
内容的提问来源于stack exchange,提问作者OfirD
相关产品推荐
相关产品推荐

