如何从IQueryable对象中检索订购信息?

时间:2010-02-05 03:05:45

标签: linq sorting expression iqueryable

让我们说,我有一个IQueryable的例子。我怎样才能找到订购的参数?

以下是OrderBy()方法的外观(作为参考):

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) }
        )
    );
}

Matt Warren的暗示:

  

所有可查询对象(甚至IOrderedQueryable)都有基于它们的表达式树,它们对它们所代表的活动进行编码。您应该找到使用IQueryable.Expression属性的方法调用表达式节点,该节点表示对Queryable.OrderBy方法的调用以及列出的实际参数。您可以从keySelector参数解码用于排序的表达式。看看调试器中的IOrderedQueryable对象实例,看看我的意思。

1 个答案:

答案 0 :(得分:4)

这不是很好,但似乎可以完成这项工作:

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 }
        }
        });

    }
}