2016-05-09 13 views
0

私のプロジェクトには、文字列でIEnumerableを並べ替えて並べ替えることができるような拡張があり、より動的に並べ替えることができます。だから、並べ替えのための式ツリーを動的に構築

、私はこれらのモデルがある場合:私のオブジェクトはICollection<ChildModel> MyChildrenのように、私はハード私の並べ替えをコーディングすることができ、ICollection性質を持っている場合は、しかし、

myList.OrderByField("RecordName "); 

myList.OrderByField("MyChild.SavedDate"); 

public MyModel 
{ 
    public int Id {get; set;} 
    public string RecordName {get; set;} 
    public ChildModel MyChild {get; set;} 
} 

public ChildModel 
{ 
    public int ChildModelId {get; set;} 
    public DateTime SavedDate {get; set;} 
} 

を、私は2つの方法で並べ替えることができますこのように:

myList 
    .OrderBy(m => m.MyChildren 
     .OrderByDescending(c => c.SavedDate).FirstOrDefault().SavedDate); 

私が欲しいものを得る。

私の質問はどのように私はこれと同じ結果を得ることができるように私の拡張メソッドを更新することができ、である:ここで

myList.OrderByField("MyChildren.SavedDate"); 

を私の現在の延長である:

public static class MkpExtensions 
{ 
    public static IEnumerable<T> OrderByField<T>(this IEnumerable<T> list, string sortExpression) 
    { 
     sortExpression += ""; 
     string[] parts = sortExpression.Split(' '); 
     bool descending = false; 
     string fullProperty = ""; 

     if (parts.Length > 0 && parts[0] != "") 
     { 
      fullProperty = parts[0]; 

      if (parts.Length > 1) 
      { 
       descending = parts[1].ToLower().Contains("esc"); 
      } 

      ParameterExpression inputParameter = Expression.Parameter(typeof(T), "p"); 
      Expression propertyGetter = inputParameter; 
      foreach (string propertyPart in fullProperty.Split('.')) 
      { 
       PropertyInfo prop = propertyGetter.Type.GetProperty(propertyPart); 
       if (prop == null) 
        throw new Exception("No property '" + fullProperty + "' in + " + propertyGetter.Type.Name + "'"); 
       propertyGetter = Expression.Property(propertyGetter, prop); 
      } 

      Expression conversion = Expression.Convert(propertyGetter, typeof(object)); 
      var getter = Expression.Lambda<Func<T, object>>(conversion, inputParameter).Compile(); 

      if (descending) 
       return list.OrderByDescending(getter); 
      else 
       return list.OrderBy(getter); 
     } 

     return list; 
    } 
} 

私が考えていましたpropのタイプをチェックし、if... elseステートメントを実行していますが、わかりません。

たぶん、このような何か:

foreach (string propertyPart in fullProperty.Split('.')) 
{ 
    var checkIfCollection = propertyGetter.Type.GetInterfaces()//(typeof (ICollection<>).FullName); 
     .Any(x => x.IsGenericType && 
      (x.GetGenericTypeDefinition() == typeof(ICollection<>) || x.GetGenericTypeDefinition() == typeof(IEnumerable<>))); 

    if (checkIfCollection) 
    { 
     // Can I get this to do something like 
     // myList.OrderBy(m => m.MyChildren.Max(c => c.SavedDate)); 

     // So far, I can get the propertyGetter type, and the type of the elements: 
     var pgType = propertyGetter.Type; 
     var childType = pgType.GetGenericArguments().Single(); 

     // Now I want to build the expression tree to get the max 
     Expression left = 
      Expression.Call(propertyGetter, pgType.GetMethod("Max", System.Type.EmptyTypes)); 
     // But pgType.GetMethod isn't working 
    } 
    else 
    { 
     PropertyInfo prop = propertyGetter.Type.GetProperty(propertyPart); 
     if (prop == null) 
      throw new Exception("No property '" + fullProperty + "' in + " + propertyGetter.Type.Name + "'"); 
     propertyGetter = Expression.Property(propertyGetter, prop); 
    } 
} 
+0

FYI、あなたのようなので、ハードコーディングされたネストされた順序付けを簡素化することができます:ここでは

は、式ツリーを通じてMaxをコールする方法の例です。 (c => c.SavedDate)); '。あなたのプログラムが '' MyChildren.SavedDate "'を見たとき、MyChildrenによって昇順に並べ替えることを知っていますが、SavedDateで降順にする方法はどうですか? – StriplingWarrior

+0

マックスは子レコードのために働くでしょう。私の文字列では、実際には 'MyChildren.SavedDate asc'や' MyChildren.SavedDate desc'を使い、それを反転させます。 '.SavedDate'は最近のものを前提としています。 ;) –

答えて

0

Max機能が拡張メソッドではなく、IEnumerableまたはICollectionのメンバメソッドです。クラスEnumerableから呼び出す必要があります。 `myList.OrderBy(M => m.MyChildren.Max:

IEnumerable<int> list = new List<int> { 3, 5, 7, 2, 12, 1 }; 
var type = typeof(Enumerable); //This is the static class that contains Max 

//Find The overload of Max that matches the list 
var maxMethod = type.GetMethod("Max", new Type[] { typeof(IEnumerable<int>) }); 
ParameterExpression p = Expression.Parameter(typeof(IEnumerable<int>)); 

//Max is static, so the calling object is null 
var exp = Expression.Call(null, maxMethod, p); 
var lambda = Expression.Lambda<Func<IEnumerable<int>, int>>(exp, p); 
Console.WriteLine(lambda.Compile()(list)); 
関連する問題