2015-05-16 13 views
6

Ich bekomme diese Ausnahme, wenn ich diesen Code ausführen.Dynamischer Lambda Ausdruck Anruf

Parameterexpression des Typs System.Int64 kann nicht für die Delegierten Parameter von System.Object Typ verwendet werden

Ich weiß, es ist etwas, mit dem Expression.Lambda<func<object,bool>> Teil des Codes zu tun. Insgesamt möchte ich jede Art von ParameterExpression in diese Methode übergeben und es wird den Ausdruck aufrufen.

public static IQueryable<T> OrderData<T>(IQueryable<T> data) 
{ 
    try 
    { 
     Order order = Order.ASC; 
     var result = Enum.TryParse<Order>(_gridSettings.SortOrder, true, out order); 
     if (_gridSettings.IsSearch) 
     { 
      data = ExpressionSort(order, data, typeof(T).GetProperty(_gridSettings.SortColumn)); 
     } 
     else 
     { 
      data = ExpressionSort(order, data, _defaultColumn); 
     } 
    } 
    catch (Exception ex) 
    { 
     log.WriteLog(MethodBase.GetCurrentMethod(), LogLevel.FATAL, ex); 
    } 
    return data; 
} 

private static IQueryable<T> ExpressionSort<T>(Order order, IQueryable<T> data, PropertyInfo property) 
{ 
    // Compose the expression tree that represents the parameter to the predicate. 
    ParameterExpression paramExpression = Expression.Parameter(property.PropertyType, property.Name); 
    IQueryable<T> queryableData = data.AsQueryable<T>(); 
    switch (order) 
    { 
     case Order.ASC: 
      return ExecuteCall(paramExpression, paramExpression, queryableData, "OrderBy"); 
     case Order.DESC: 
      return ExecuteCall(paramExpression, paramExpression, queryableData, "OrderByDescending"); 
    } 
    return data; 
} 

private static IQueryable<T> ExecuteCall<T>(Expression expression, ParameterExpression paramExpression, IQueryable<T> queryableData, string linqMethod) 
{ 
    MethodCallExpression callExpression = Expression.Call(
           typeof(Queryable), 
           linqMethod, 
           new Type[] { queryableData.ElementType }, 
           queryableData.Expression, 
           Expression.Lambda<Func<object, bool>>(expression, new ParameterExpression[] { paramExpression })); 
    // Create an executable query from the expression tree. 
    return queryableData.Provider.CreateQuery<T>(callExpression); 
} 

EDIT: ich diese Antwort auf eine ähnliche Frage sah

Expression of type 'System.Int32' cannot be used for return type 'System.Object' Ich weiß nicht, wie es aber

EDIT 2 zu meinen Code anwenden: Die Hauptproblem ist, dass diese Expression.Lambda<Func<object, bool>>(conversion, new ParameterExpression[] { paramExpression })); Zeile gibt mir eine Ausnahme. paramExpression enthält ein Int64, aber es erwartet ein Objekt. Ich weiß nicht, wie ich den Func dynamisch von den Informationen, die ich bereits habe, erzählen kann oder ob das möglich ist.

ZIEL: ich so etwas wie diese data.OrderBy(x=>x.DynamicProperty);

+0

Sie verwenden T und Ihre Parameter eine Struktur ist ... Sie können nicht eine Struktur gegossen Objekt –

+0

Können Sie ein Beispiel schreiben, wie Sie diese verwenden, wie es richtig steht jetzt? Ohne das ist es schwierig zu beurteilen, wie es zu beheben ist. –

+0

Ich postete die Methode die anderen beiden Methoden aufrufen. Es gibt nichts Höheres als jetzt, nur weil es noch nicht funktioniert. "IQueryable Daten" wäre so etwas wie "Liste Daten" @EBrown – imGreg

Antwort

3

Dies ist, was Sie gefragt, glaube ich ... Ich habe es getestet und es scheint zu funktionieren.

// Caching of the reflection 
private static readonly MethodInfo orderByMethod = GetOrderByMethod("OrderBy"); 
private static readonly MethodInfo orderByDescendingMethod = GetOrderByMethod("OrderByDescending"); 

private static IOrderedQueryable<TSource> ExpressionSort<TSource>(Order order, IQueryable<TSource> source, PropertyInfo property) 
{ 
    // Compose the expression tree that represents the parameter to 
    // the predicate. 

    // The expression you would use is source => source.Property, 

    // The parameter of the lambda, source 
    ParameterExpression sourceExpression = Expression.Parameter(typeof(TSource), "source"); 

    // Accessing the expression 
    MemberExpression propertyExpression = Expression.Property(sourceExpression, property); 

    // The full lambda expression. We don't need the 
    // Expression.Lambda<>, but still the keySelector will be an 
    // Expression<Func<,>>, because Expression.Lambda does it 
    // authomatically. LambdaExpression is simply a superclass of 
    // all the Expression<Delegate> 
    LambdaExpression keySelector = Expression.Lambda(propertyExpression, sourceExpression); 

    // The OrderBy method we will be using, that we have cached 
    // in some static fields 
    MethodInfo method = order == Order.ASC ? orderByMethod : orderByDescendingMethod; 

    // Adapted from Queryable.OrderBy (retrieved from the reference 
    // source code), simply changed the way the OrderBy method is 
    // retrieved to "method" 
    return (IOrderedQueryable<TSource>)source.Provider.CreateQuery<TSource>(Expression.Call(null, method.MakeGenericMethod(new Type[] 
    { 
     typeof(TSource), 
     property.PropertyType 
    }), new Expression[] 
    { 
     source.Expression, 
     Expression.Quote(keySelector) 
    })); 
} 

private static MethodInfo GetOrderByMethod(string methodName) 
{ 
    // Here I'm taking the long and more correct way to find OrderBy/ 
    // OrderByDescending: looking for a public static method with the 
    // right name, with two generic arguments and that has the 
    // parameters related to those two generic arguments in a certain 
    // way (they must be IQueryable<arg0> and Expression<Func<arg0, 
    // arg1>> 
    MethodInfo orderByMethod = (from x in typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public) 
           where x.Name == methodName 
           let generics = x.GetGenericArguments() 
           where generics.Length == 2 
           let parameters = x.GetParameters() 
           where parameters.Length == 2 && 
            parameters[0].ParameterType == typeof(IQueryable<>).MakeGenericType(generics[0]) && 
            parameters[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(generics)) 
           select x).Single(); 

    return orderByMethod; 
} 

Bitte immer nicht AsQueryable<>() verwenden. Es tut nicht, was Sie denken, und es ist völlig nutzlos außerhalb der Unit-Tests und sehr spezifische Anwendungsfälle.

+0

Das ist genau das, was ich wollte. Ich wusste nicht, wie kompliziert es war. – imGreg

+0

Es wäre auch schön, wenn Sie Informationen veröffentlichen würden, die erklären würden, warum das so funktioniert. –

+0

@EBrown Hat alles kommentiert. – xanatos

0

Sie könnten meine OrderByString-Erweiterung verwenden. https://www.nuget.org/packages/OrderByString/ Es nimmt Strings für Sortierparameter. Die Sortierparameter-Strings können durch Kommas getrennte Listen von Eigenschaftsnamen wie "Prop1, Prop2" oder eine Sortierreihenfolge wie in "Prop1 DESC, Prop2 ASC" sein.

using OrderByExtensions; 

public static IQueryable<T> OrderData<T>(IQueryable<T> data) 
{ 
    try 
    { 
     Order order = Order.ASC; 
     var result = Enum.TryParse<Order>(_gridSettings.SortOrder, true, out order); 

     var sortColumn = _gridSettings.IsSearch ? _gridSettings.SortColumn : _defaultColumn; 

     data = data.OrderBy(sortColumn + " " + _gridSettings.SortOrder.ToString()); 
    } 
    catch (Exception ex) 
    { 
     log.WriteLog(MethodBase.GetCurrentMethod(), LogLevel.FATAL, ex); 
    } 
    return data; 
} 

ODER

Sie könnten die folgende GetExpressionForProperty Methode verwenden, die die erwartete Sortierausdruck für OrderBy, OrderByDescending, ThenBy oder ThenByDescending zurückgibt.

private static IQueryable<T> ExpressionSort<T>(Order order, IQueryable<T> data, PropertyInfo property) 
{ 
    Expression<Func<T, object>> propertyExpression = GetExpressionForProperty<T>(property); 

    return order == Order.DESC ? data.OrderByDescending(propertyExpression) : data.OrderBy(propertyExpression); 
} 

static Expression<Func<TSource, object>> GetExpressionForProperty<TSource>(PropertyInfo propertyInfo) 
{ 
    var param = Expression.Parameter(typeof(TSource)); 

    return Expression.Lambda<Func<TSource, object>>(
     Expression.Convert(
      Expression.Property(param, propertyInfo), 
      typeof(object) 
     ) 
     , param); 
}