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

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

马特沃伦的提示:

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 call 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.

最佳答案

这并不漂亮,但它似乎可以完成这项工作:

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

    }
}

关于linq - 如何从 IQueryable 对象检索订购信息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2204780/

相关文章:

c# - LINQ to XML 查询中的格式错误

swift - 使用带有逻辑 AND 运算符 && 的 Swift if let

xpath - 有没有办法正确连接三个任意 XPath 表达式以生成新的有效 XPath 表达式?

c# - 由于表达式树不可序列化,如何使用无法使用 WCF 工作的表达式树?

c# - 在特定领域不同

c# - 选择 List<int> 中所有值的位置

c# - 线程安全的缓存枚举器-使用yield锁定

Python - 将字典列表中的元素移动到列表的末尾

sql - 排序时将一个选项强制置于列表顶部

arrays - 在排序数组上应用函数