2015-05-16 3 views
6

Я получаю это исключение, когда запускаю этот код.Динамический вызов выражения Lambda

ParameterExpression типа System.Int64 не может быть использован для параметра делегата типа System.Object

Я знаю, что это что-то делать с Expression.Lambda<func<object,bool>> части кода. В общем, я хочу передать любой тип ParameterExpression в этот метод, и он вызовет выражение.

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: я видел этот ответ на аналогичный вопрос

Expression of type 'System.Int32' cannot be used for return type 'System.Object' Я не знаю, как применить его к моему коду, хотя

EDIT 2: The Основная проблема заключается в том, что эта строка Expression.Lambda<Func<object, bool>>(conversion, new ParameterExpression[] { paramExpression })); дает мне исключение. paramExpression содержит Int64, но его ожидает объект. Я не знаю, как динамически сообщать Func из информации, которую я уже имею, или если это возможно.

ЦЕЛЬ: Я пытаюсь сделать что-то вроде этого data.OrderBy(x=>x.DynamicProperty);

+0

Вы используете T <объект, BOOL> и ваш параметр является структурой ... вы не можете бросить на объект-структуру –

+0

Вы можете разместить пример того, как вы используете это, как она стоит Теперь? Без этого трудно оценить, как это исправить. –

+0

Я отправил метод вызова двух других методов. Сейчас нет ничего выше, только потому, что он еще не работает. 'IQueryable data' будет что-то вроде этого' List data' @EBrown – imGreg

ответ

3

Это то, что вы просили, я думаю ... Я проверил это, и это, кажется, работает.

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

Пожалуйста, никогда не используйте AsQueryable<>(). Он не делает то, что вы думаете, и это абсолютно бесполезно за пределами модульного тестирования и очень конкретных случаев использования.

+0

Это именно то, что я хотел. Я не понимал, насколько это было сложно. – imGreg

+0

Также было бы неплохо, если бы вы разместили информацию, которая объясняла бы, почему это работает над другим. –

+0

@EBrown Прокомментировал все. – xanatos

0

Вы можете использовать расширение OrderByString. https://www.nuget.org/packages/OrderByString/ Требуется строка для параметров сортировки. Строки параметров сортировки могут быть разделены запятыми списками имен свойств, такими как «Prop1, Prop2» или могут содержать порядок сортировки, как в «Prop1 DESC, Prop2 ASC».

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

ИЛИ

Вы можете использовать следующий метод, который возвращает GetExpressionForProperty ожидаемое выражение сортировки для OrderBy, OrderByDescending, ThenBy или ThenByDescending.

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); 
} 
Смежные вопросы