2016-05-09 18 views
0

für die Sortierung ich eine Erweiterung in meinem Projekt, das ich Lassen Sie sich ein IEnumerable mit einer Schnur sortieren, so dass das Sortieren dynamischer erfolgen.Dynamisch einen Ausdruck Baum bauen

Also, wenn ich diese Modelle haben:

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

kann ich zwei Arten sortieren:

myList.OrderByField("RecordName "); 

myList.OrderByField("MyChild.SavedDate"); 

Wenn jedoch meine Aufgabe, eine ICollection Eigenschaft hat, wie ICollection<ChildModel> MyChildren ich meine Art hart codieren wie folgt:

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

Und bekommen, was ich will.

Meine Frage ist, wie kann ich meine Erweiterungsmethode aktualisieren, um die gleichen Ergebnisse mit diesem zu ermöglichen, zu erhalten:

myList.OrderByField("MyChildren.SavedDate"); 

Hier ist meine aktuelle Erweiterung:

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

Ich dachte die Art der prop zu kontrollieren und dabei eine if... else Aussage, aber ich bin mir nicht sicher.

Vielleicht so etwas wie:

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, können Sie die hartcodierte verschachtelte Anordnung vereinfachen etwa so: 'myList.OrderBy (m => m.MyChildren.Max (c => c.SavedDate)); '. Apropos, wenn Ihr Programm sieht '„MyChildren.SavedDate“', wie ist es denn wissen von MyChildren zu sortieren aufsteigend, aber absteigend nach SavedDate? – StriplingWarrior

+0

Max wird für die untergeordneten Datensätze funktionieren. In meinem String verwende ich 'MyChildren.SavedDate asc' tatsächlich oder' MyChildren.SavedDate desc' und Flip accordingling. '.SavedDate' nimmt zuletzt ... an. ;) –

Antwort

0

Die Max Funktion ist eine Erweiterung Methode, kein Mitglied Verfahren von IEnumerable oder ICollection. Sie müssen es von seiner Klasse Enumerable nennen. Hier

ist ein Beispiel dafür, wie Max durch Ausdrucksbaum nennen:

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