从IQueryable <T>中删除OrderBy

Tra*_*den 11 .net c# linq iqueryable linq-to-sql

我有一个分页API,它返回用户请求的行,但一次只有这么多,而不是整个集合.API按设计工作,但我必须计算可用的记录总数(正确的页面计算).在API中,我使用Linq2Sql,在我最终提出请求之前,我在IQueryable上工作了很多.当我去计算时,我会调用类似于:totalRecordCount = queryable.Count();

结果SQL很有意思,但它也增加了一个不必要的Order By,这使得查询非常昂贵.

exec sp_executesql N'SELECT COUNT(*) AS [value]
FROM (
    SELECT TOP (1) NULL AS [EMPTY]
    FROM [dbo].[JournalEventsView] AS [t0]
    WHERE [t0].[DataOwnerID] = @p0
    ORDER BY [t0].[DataTimeStamp] DESC
    ) AS [t1]',N'@p0 int',@p0=1
Run Code Online (Sandbox Code Playgroud)

因为我正在使用IQueryable,所以我可以在它进入SQL服务器之前操作IQueryable.

我的问题是,如果我已经有一个带有OrderBy的IQueryable,是否可以在调用Count()之前删除该OrderBy?

喜欢:totalRecordCount = queryable.NoOrder .Count();

如果没有,没有大事.我看到很多关于OrderBy的问题,但没有涉及从Linq表达式中删除OrderBy的任何问题.

谢谢!

Cra*_*son 7

因此,下面的代码是针对内存数组的尖峰.使用Entity Framework(或其他任意IQueryProvider实现)可能存在一些障碍.基本上,我们要做的是访问表达式树并查找任何Ordering方法调用,并将其从树中删除.希望这能指出你正确的方向.

class Program
{
    static void Main(string[] args)
    {
        var seq = new[] { 1, 3, 5, 7, 9, 2, 4, 6, 8 };

        var query = seq.OrderBy(x => x);

        Console.WriteLine("Print out in reverse order.");
        foreach (var item in query)
        {
            Console.WriteLine(item);
        }

        Console.WriteLine("Prints out in original order");
        var queryExpression = seq.AsQueryable().OrderBy(x => x).ThenByDescending(x => x).Expression;

        var queryDelegate = Expression.Lambda<Func<IEnumerable<int>>>(new OrderByRemover().Visit(queryExpression)).Compile();

        foreach (var item in queryDelegate())
        {
            Console.WriteLine(item);
        }


        Console.ReadLine();
    }
}

public class OrderByRemover : ExpressionVisitor
{
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.DeclaringType != typeof(Enumerable) && node.Method.DeclaringType != typeof(Queryable))
            return base.VisitMethodCall(node);

        if (node.Method.Name != "OrderBy" && node.Method.Name != "OrderByDescending" && node.Method.Name != "ThenBy" && node.Method.Name != "ThenByDescending")
            return base.VisitMethodCall(node);

        //eliminate the method call from the expression tree by returning the object of the call.
        return base.Visit(node.Arguments[0]);
    }
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*ers 6

不仅有一个不需要的ORDER BY,还有一个假的TOP(1).

SELECT TOP (1) NULL AS [EMPTY] ...
Run Code Online (Sandbox Code Playgroud)

该子选择只返回0或1行.事实上,如果没有TOP,那么在子选择中使用ORDER BY是不合法的.

ORDER BY子句在视图,内联函数,派生表,子查询和公用表表达式中无效,除非还指定了TOP或FOR XML:SELECT COUNT(*)FROM(SELECT*FROM Table1 ORDER BY foo)

sqlfiddle

我想你的LINQ可能做错了..Take(1)在打电话之前,你确定你的查询中某处没有写过或类似.Count()吗?

这是错的:

IQueryable<Foo> foo = (...).OrderBy(x => x.Foo).Take(1);
int count = foo.Count();
Run Code Online (Sandbox Code Playgroud)

你应该这样做:

IQueryable<Foo> foo = (...);
Iqueryable<Foo> topOne = foo.OrderBy(x => x.Foo).Take(1);
int count = foo.Count();
Run Code Online (Sandbox Code Playgroud)