6

I'm getting this exception when I run this code.

ParameterExpression of type System.Int64 cannot be used for delegate parameter of type System.Object

I know it's something to do with the Expression.Lambda<func<object,bool>> part of the code. Overall, I want to pass any type of ParameterExpression into this method and it will call the expression.

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: I did see this answer to a similar question

Expression of type 'System.Int32' cannot be used for return type 'System.Object' I do not know how to apply it to my code though

EDIT 2: The main issue is that thisExpression.Lambda<Func<object, bool>>(conversion, new ParameterExpression[] { paramExpression })); line is giving me an exception. paramExpression contains an Int64 but its expectinng an object. I dont know how to dynamically tell the Func from the information I already have or if that is possible.

GOAL: I am trying to do something like this data.OrderBy(x=>x.DynamicProperty);

9
  • You are using T<object, bool> and your parameter is a struct... you can't cast an struct to object Commented May 16, 2015 at 3:29
  • Can you post an example of how you are using this, as it stands right now? Without that it's difficult to assess how to fix it. Commented May 16, 2015 at 4:25
  • I posted the method call the other two methods. There is nothing higher than that now only because it doesnt work yet. IQueryable<T> data would be something like this List<MyClass> data @EBrown Commented May 16, 2015 at 4:28
  • And what would MyClass look like? What is _gridSettings? What is _defaultColumn? Commented May 16, 2015 at 4:29
  • To keep the example simple MyClass would just contain prop1 which is an int. _gridSettings is just an object that will tell me which column to order by asc or desc. defaultColumn is PropertyInfo of a column. Commented May 16, 2015 at 4:34

3 Answers 3

3

This is what you asked for, I think... I've tested it and it seems to work.

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

Please don't ever use AsQueryable<>(). It doesn't do what you think, and it is totally useless outside unit testing and very specific use cases.

Sign up to request clarification or add additional context in comments.

3 Comments

This is exactly what I wanted. I did not realize how complicated it was.
Also, it would be nice if you would post information that would explain why this works over the other.
@EBrown Commented everything.
1

You could use my OrderByString extension. https://www.nuget.org/packages/OrderByString/ It takes strings for sort parameters. The sort parameters strings can be comma-delimited lists of property names, such as "Prop1,Prop2" or it can include a sort order as in "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;
}

OR

You could use the following GetExpressionForProperty method that returns the expected sort expression for OrderBy, OrderByDescending, ThenBy, or 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);
}

Comments

0

Try using Expression.Convert. Here's a similar question that may give you some more guidance:

Expression of type 'System.Int32' cannot be used for return type 'System.Object'

1 Comment

I did see that answer there but I did not how to apply that answer to my code. I dont know what to put into Func<this part,bool>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.