2010-02-05 58 views
4

比方說,我有一個IQueryable的實例。我怎樣才能找出它被訂購的參數?如何從IQueryable對象中檢索訂購信息?

這裏是如何排序依據()方法看起來像(作爲參考):

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

從馬特·沃倫提示:

所有queryables(甚至IOrderedQueryable的)都表達樹背後他們,編碼他們所代表的活動。您應該找到使用IQueryable.Expression屬性的方法調用表達式節點,該節點表示使用列出的實際參數調用Queryable.OrderBy方法。您可以從keySelector參數解碼用於排序的表達式。查看調試器中的IOrderedQueryable對象實例,瞭解我的意思。

回答

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

    } 
}