'Create an expression dynamically to sort Linq to Entities ascending/descending based on a dynamically provided property name
Here is what I am trying to do. I have done a few simple expressions, but this one is a little too much for me right now.
public static Expression<Func<IQueryable<TEntityType>, IOrderedQueryable<TEntityType>>> SortMeDynamically<TEntityType>(bool isAsc, string propertyname)
{
var param = Expression.Parameter(typeof(TEntityType), "x");
var prop = Expression.PropertyOrField(param, propertyname);
var sortLambda = Expression.Lambda(prop, param);
string sortOrder = isAsc ? "OrderBy" : "OrderByDescending";
var selector = Call(
typeof(Queryable),
sortOrder,
new[] { prop.Type},
sortLambda);
var lambda = Lambda<Func<IQueryable<TEntityType>, IOrderedQueryable<TEntityType>>>(selector, param);
return lambda;
}
The error I get is the following.
System.InvalidOperationException: No generic method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic
The closest solution I found was the one below.
Solution 1:[1]
Calling the various .OrderBy
linq methods dynamically is quite a pain.
The most difficult part of this process is locating the MethodInfo
of the four relevant Queryable.OrderBy
methods, with the correct generic constraints. There are a number of ways to achieve this.
You could pull the method out of a template Expression<Func<...>>
, as in your linked answer;
Expression<Func<IOrderedEnumerable<TEntityType>>> sortMethod =
(() => query.OrderBy<TEntityType, object>(k => null));
var methodCallExpression = (sortMethod.Body as MethodCallExpression);
var method = methodCallExpression.Method.GetGenericMethodDefinition();
You could use Type.GetMethods
and filter the results. Though you'd have to worry about future runtime changes breaking this approach.
var method = typeof(Queryable).GetMethods()
.Where(m => m.IsGenericMethod
&& m.Name == nameof(Queryable.OrderBy)
&& m.GetParameters().Length == 2)
.Single();
In both cases you'd then need to call .MakeGenericMethod
to supply the correct generic parameters.
var genericSortMethod = method.MakeGenericMethod(typeof(TEntityType), prop.Type);
Or you could create a delegate and pull the method from there. Again, getting the generic constraints correct is a bit fiddly. But can be easier with a helper method. Which is similar to how the linq runtime locates this MethodInfo
.
public MethodInfo GetOrderFunc<T, V>(Func<IQueryable<T>, Expression<Func<T, V>>, IOrderedQueryable<T>> func)
=> func.Method;
var genericSortMethod = GetOrderFunc<TEntityType, V>(Queryable.OrderBy);
If you don't know the argument value type, you could call that method via reflection.
Now you can either invoke the method;
var orderedQuery = (IOrderedQueryable<TEntityType>)genericSortMethod.Invoke(null, new object[] { query, sortLambda });
Or by reading the source code, recreate what those methods actually do.
var expression = query.Expression;
expression = Expression.Call(
typeof(Queryable),
genericSortMethod,
new Type[] { typeof(TEntityType), prop.Type },
expression,
Expression.Quote(sortLambda));
var orderedQuery = (IOrderedQueryable<T>)query.Provider.CreateQuery<T>(expression);
No matter how you approach this, you need to take an IQueryable<TEntity> query
parameter, and return an IOrderedQueryable<TEntity>
. Or just stop at creating the Expression<Func<>>
.
The other, other option is to move all the generic mucking around into a helper method. Then invoke that method via reflection. Obtaining the generic MethodInfo
using one of the same approaches explained above.
public IOrderedQueryable<T> Order<T, V>(this IQueryable<T> query, Expression<Func<T, V>> key, bool then, bool desc)
=> (desc)
? (then ? ((IOrderedQueryable<T>)query).ThenByDescending(key) : query.OrderByDescending(key))
: (then ? ((IOrderedQueryable<T>)query).ThenBy(key) : query.OrderBy(key));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | marc_s |