5

I am trying to write a LINQ query to orderBy a dynamic property given by a string value.

Here is what my original code was:

 Expression<Func<T, dynamic>> orderBy = i => i.GetType().GetProperty("PropertyName").GetValue(null);

When I tried to run this orderBy I got the following exception:

LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression.

I am trying to work around this by creating an expression tree that will give me the same result. The code should be able to return any type depending on the parameter but I'm having trouble with the return type. If I don't convert the value I get a different error saying w Nullable DateTime can't be converted to Object. Here is the code that I have so far:

ParameterExpression pe = Expression.Parameter(typeof(T), "s");
Expression<Func<T, dynamic>> orderByExpression = Expression.Lambda<Func<T, dynamic>>(Expression.Convert(Expression.Property(pe, "PropertyName"), typeof(object)), pe);

and my new exception:

Unable to cast the type 'System.Nullable`1[[System.DateTime]]' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.

How would I write this expression tree to do return a dynamic type? Also is there a better way I should be doing this in LINQ?

Seth Denburg
  • 144
  • 1
  • 10
  • Check this out [get property of inner object](http://stackoverflow.com/questions/34416134/get-property-of-inner-object/34642708#34642708) – Ivan Stoev Jan 12 '16 at 23:21
  • 1
    Have a look at [Marc Gravells Dynamic OrderBy](http://stackoverflow.com/a/233505/3411327) – user3411327 Jan 13 '16 at 07:59

1 Answers1

7

There are no template arguments for Expression<Func<T, TT>> which will allow you to represent both reference types and value types. Of course, you can build the expressions, but you must interact with them via reflection.

This will properly sort a collection:

IOrderedEnumerable<TEntityType> SortMeDynamically<TEntityType>(IEnumerable<TEntityType> query, string propertyname)
{
    var param = Expression.Parameter(typeof(TEntityType), "s");
    var prop = Expression.PropertyOrField(param, propertyname);
    var sortLambda = Expression.Lambda(prop, param);

    Expression<Func<IOrderedEnumerable<TEntityType>>> sortMethod = (() => query.OrderBy<TEntityType, object>(k => null));

    var methodCallExpression = (sortMethod.Body as MethodCallExpression);
    if (methodCallExpression == null)
        throw new Exception("Oops");

    var method = methodCallExpression.Method.GetGenericMethodDefinition();
    var genericSortMethod = method.MakeGenericMethod(typeof(TEntityType), prop.Type);
    var orderedQuery = (IOrderedEnumerable<TEntityType>)genericSortMethod.Invoke(query, new object[] { query, sortLambda.Compile() });

    return orderedQuery;
}

Or if you want it on an IQueryable (if you're using EF, for example)

IOrderedQueryable<TEntityType> SortMeDynamically<TEntityType>(IQueryable<TEntityType> query, string propertyname)
{
    var param = Expression.Parameter(typeof(TEntityType), "s");
    var prop = Expression.PropertyOrField(param, propertyname);
    var sortLambda = Expression.Lambda(prop, param);

    Expression<Func<IOrderedQueryable<TEntityType>>> sortMethod = (() => query.OrderBy<TEntityType, object>(k => null));

    var methodCallExpression = (sortMethod.Body as MethodCallExpression);
    if (methodCallExpression == null)
        throw new Exception("Oops");

    var method = methodCallExpression.Method.GetGenericMethodDefinition();
    var genericSortMethod = method.MakeGenericMethod(typeof(TEntityType), prop.Type);
    var orderedQuery = (IOrderedQueryable<TEntityType>)genericSortMethod.Invoke(query, new object[] { query, sortLambda });

    return orderedQuery;
}
Rob
  • 25,569
  • 15
  • 73
  • 87