开发者

How to retrieve ordering information from IQueryable object?

Let's say, I have an instance of IQueryable. How can I found out by which parameters it was ordered?

Here is how OrderBy() method looks like (as a reference):

public static IOrderedQueryable<T> OrderBy<T, TKey>(
    this IQueryable<T> source, Expression<Func<T, TKey>> keySelector)
{
    return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(
        Expression.Call(null,
            ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(T), typeof(TKey) }
            ),
            new Expression[] { source.Expression, Expression.Quote(keySelector) }
        )
    );
}

A hint from Matt Warren:

All queryables (even IOrderedQueryable's) have expression trees underlying them that encode the activity they represent. You should find using the IQueryable.Expression property a method-call expression node representing a c开发者_StackOverflow中文版all to the Queryable.OrderBy method with the actual arguments listed. You can decode from the keySelector argument the expression used for ordering. Take a look at the IOrderedQueryable object instance in the debugger to see what I mean.


This isn't pretty, but it seems to do the job:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Windows.Forms;

public class Test
{
    public int A;
    public string B { get; set; }
    public DateTime C { get; set; }
    public float D;
}
public class QueryOrderItem
{
    public QueryOrderItem(Expression expression, bool ascending)
    {
        this.Expression = expression;
        this.Ascending = ascending;
    }
    public Expression Expression { get; private set; }
    public bool Ascending { get; private set; }
    public override string ToString()
    {
        return (Ascending ? "asc: " : "desc: ") + Expression;
    } 
}
static class Program
{

    public static List<QueryOrderItem> GetQueryOrder(Expression expression)
    {
        var members = new List<QueryOrderItem>(); // queue for easy FILO
        GetQueryOrder(expression, members, 0);
        return members;
    }
    static void GetQueryOrder(Expression expr, IList<QueryOrderItem> members, int insertPoint)
    {
        if (expr == null) return;
        switch (expr.NodeType)
        {
            case ExpressionType.Call:
                var mce = (MethodCallExpression)expr;
                if (mce.Arguments.Count > 1)
                {   // OrderBy etc is expressed in arg1
                    switch (mce.Method.Name)
                    { // note OrderBy[Descending] shifts the insertPoint, but ThenBy[Descending] doesn't
                        case "OrderBy": // could possibly check MemberInfo
                            members.Insert(insertPoint, new QueryOrderItem(mce.Arguments[1], true));
                            insertPoint = members.Count; // swaps order to enforce stable sort
                            break;
                        case "OrderByDescending":
                            members.Insert(insertPoint, new QueryOrderItem(mce.Arguments[1], false));
                            insertPoint = members.Count;
                            break;
                        case "ThenBy":
                            members.Insert(insertPoint, new QueryOrderItem(mce.Arguments[1], true));
                            break;
                        case "ThenByDescending":
                            members.Insert(insertPoint, new QueryOrderItem(mce.Arguments[1], false));
                            break;
                    }
                }
                if (mce.Arguments.Count > 0)
                {   // chained on arg0
                    GetQueryOrder(mce.Arguments[0], members, insertPoint);
                }
                break;
        }
    }
    static void Main()
    {
        var data = new[] {
            new Test { A = 1, B = "abc", C = DateTime.Now, D = 12.3F},
            new Test { A = 2, B = "abc", C = DateTime.Today, D = 12.3F},
            new Test { A = 1, B = "def", C = DateTime.Today, D = 10.1F}
        }.AsQueryable();
        var ordered = (from item in data
                       orderby item.D descending
                       orderby item.C
                       orderby item.A descending, item.B
                       select item).Take(20);
        // note: under the "stable sort" rules, this should actually be sorted
        // as {-A, B, C, -D}, since the last order by {-A,B} preserves (in the case of
        // a match) the preceding sort {C}, which in turn preserves (for matches) {D}

        var members = GetQueryOrder(ordered.Expression);
        foreach (var item in members)
        {
            Console.WriteLine(item.ToString());
        }

        // used to investigate the tree
        TypeDescriptor.AddAttributes(typeof(Expression), new[] {
            new TypeConverterAttribute(typeof(ExpandableObjectConverter)) });
        Application.Run(new Form
        {
            Controls = { 
            new PropertyGrid { Dock = DockStyle.Fill, SelectedObject = ordered.Expression }
        }
        });

    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜