2009-04-02 6 views
3

Ich erstelle einen dynamischen Ausdruck, der Elemente in einer Liste nach einer Regel (Lambda exp.) Sortiert. Dies ist der Code:Dynamisch erzeugte Ausdrücke

Expression<Func<String, String>> exp = o => o; 

MethodCallExpression orderByExp = Expression.Call(typeof(Enumerable), "OrderBy", 
    new Type[] { typeof(String), exp.Body.Type }, Expression.Parameter(typeof(IEnumerable<String>), "list"), exp); 

Jetzt habe ich zuvor erstellte Ausdruck auf bestimmte Daten ausführen möchten es zu sortieren, aber es funktioniert nicht, weil einige seltsame Ausnahmen wie „Lambda Parameter nicht in scope“ oder „Argument Ausdruck ist nicht gültig ".

var data = new String[] { "asdasdasd", "asdads", "123", "xcvxcvs", "ASDSD" }; 

// one of attempts: doesn't work 
var result = data.AsQueryable().Provider.CreateQuery<String>(orderByExp); 

Kann jemand mir dabei helfen?

Antwort

2

Dies ist der Arbeitscode:

Expression<Func<String, String>> exp = o => o; 
var list = Expression.Parameter(typeof(IEnumerable<String>), "list"); 

MethodCallExpression orderByExp = Expression.Call(typeof(Enumerable), "OrderBy", 
    new Type[] { typeof(String), exp.Body.Type }, list, exp); 

var lambda = Expression.Lambda<Func<IEnumerable<String>, IEnumerable<String>>>(orderByExp, list); 
var data = new String[] { "asdasdasd", "asdads", "123", "xcvxcvs", "ASDSD" }; 
var result = lambda.Compile()(data); 
  1. Um die Method führen Sie es in Lambda-Ausdruck wickeln sollte.
  2. Stellen Sie sicher, dass Sie beim Erstellen einer MethodCallExpression und LambdaExpression die gleiche Instanz des Parameterausdrucks ('list') verwenden, und nicht zwei separate Instanzen, auch wenn sie denselben Namen haben. Andernfalls erhalten Sie: "Lambda Parameter nicht im Bereich" Ausnahme ohne viel Erklärung.

Dank Experten

0

Gibt es einen bestimmten Grund, dass Sie nicht nur telefonieren:

data.AsQueryable().OrderBy(exp); 

Sie benötigen sogar IQueryable hier zu benutzen? Ich habe das Gefühl, dass ich etwas vom großen Ganzen vermisse. Werden Sie dies tatsächlich als Teil von LINQ to SQL (oder LINQ to Entities) nennen? Wenn es nur innerhalb von LINQ to Objects ist, können Sie nicht einfach data.OrderBy(exp) verwenden?

Grundsätzlich wäre etwas mehr Erklärung hilfreich sein :)

+0

Das ganze Bild ist weiter: ich cre wollen aß eine Abfrage (Regeln wie Reihenfolge, wo, kann sonst sein) auf einige Daten, die ich gerade nicht habe. Aber ich kenne seinen Typ. Diese Abfrage wird später an einen Webdienst gesendet, der über die Daten verfügt und die Abfrage ausführen wird. – Kamarey

+0

und vergessen, das ist eine einfache LINQ to Objects. – Kamarey

+0

Okay, jetzt bin ich verwirrt - wenn Sie die Abfrage an einen Webdienst senden, klingt es nicht so, als wäre es wirklich LINQ to Objects. Was möchten Sie beim Bestellen tun - der Webservice oder der lokale Prozess? –

3

um jeden zählbare durch eine Eigenschaft (keine Reflexion):

public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> items, string property, bool ascending) 
     { 
      var MyObject = Expression.Parameter(typeof (T), "MyObject"); 
      var MyEnumeratedObject = Expression.Parameter(typeof (IEnumerable<T>), "MyEnumeratedObject"); 
      var MyProperty = Expression.Property(MyObject, property); 
      var MyLamda = Expression.Lambda(MyProperty, MyObject); 
      var MyMethod = Expression.Call(typeof(Enumerable), ascending ? "OrderBy" : "OrderByDescending", new[] { typeof(T), MyLamda.Body.Type }, MyEnumeratedObject, MyLamda); 
      var MySortedLamda = Expression.Lambda<Func<IEnumerable<T>, IOrderedEnumerable<T>>>(MyMethod, MyEnumeratedObject).Compile(); 
      return MySortedLamda(items); 
     } 
+0

Ja, es ist ein guter Weg.Aber in meiner Situation muss ich mehr als nur durch den Namen der Eigenschaft sortieren/filtern (mein Beispiel ist nur ein einfacher Fall, um das andere Problem zu erklären). Ich muss ein einfaches Lambda wie o => o.Users.Any ausführen (user => user.Type == 1). Abgesehen davon ist deine Lösung in Ordnung. Bekomme meine +1 :) – Kamarey

1

ich mit Linq das fast dem gleichen Problem gearbeitet hatte, habe ich beschlossen, eine Erweiterungsfunktion zu schreiben, und einige der Ideen aus den Antworten dieser Frage getroffen wurden:

<Extension()> _ 
Public Function OrderBy(Of T)(ByVal query As IEnumerable(Of T), ByVal sortColumn As String, ByVal direction As String) As IEnumerable(Of T) 
     Dim methodName As String = String.Format("OrderBy{0}", If(direction.ToLower() = "asc", "", "Descending")) 
     Dim parameter As ParameterExpression = Expression.Parameter(GetType(T), "p") 
     Dim memberAccess As MemberExpression = Nothing 

     For Each _property As Object In sortColumn.Split(".") 
      memberAccess = MemberExpression.Property(If(memberAccess, CType(parameter, Expression)), _property) 
     Next 

     Dim orderByLambda As LambdaExpression = Expression.Lambda(memberAccess, parameter) 
     ' 
     Dim myEnumeratedObject As ParameterExpression = Expression.Parameter(GetType(IEnumerable(Of T)), "MyEnumeratedObject") 

     Dim result As MethodCallExpression = Expression.Call(GetType(Enumerable), _ 
        methodName, _ 
        New System.Type() {GetType(T), memberAccess.Type}, _ 
        myEnumeratedObject, _ 
        orderByLambda) 

     Dim lambda = Expression.Lambda(Of Func(Of IEnumerable(Of T), IEnumerable(Of T)))(result, myEnumeratedObject) 
     Return lambda.Compile()(query) 
    End Function 
Verwandte Themen