This question already has an answer here:
-
Dynamic LINQ OrderBy on IEnumerable<T> / IQueryable<T>
19 answers
-
How to use a string to create a EF order by expression?
1 answer
I want to be able to get an OrderBy
query working with a lambda expression so that I get a SQL query with the TOP(n) key word (big performance boost).
I am able to do this if I specifiy ...
PaginatedList = query.OrderBy(x => x.QuoteID).Skip(() => skipValue).Take(() => pageSize)
But because I want the orderBy field to be dynamic through a UI selection of a name I want to do something like this:
var propertyInfo = typeof(Data.Quote).GetProperty(sortName);
Expression<Func<Data.Quote, object>> orderField = x => propertyInfo.GetValue(x, null);
PaginatedList = query.OrderBy(orderField).Skip(() => skipValue).Take(() => pageSize)
This gives me the error:
"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 tried this that's not of type Expression<Func<T, object>>
var propertyInfo = typeof(Data.Quote).GetProperty(sortName);
Func<Data.Quote, object> orderField = x => propertyInfo.GetValue(x, null);
PaginatedList = query.OrderBy(x => orderField).Skip(() => skipValue).Take(() => pageSize)
And I get this error:
"Unable to create a constant value of type [...]. Only primitive types
or enumeration types are supported in this context"
I'm sure there is a way to achieve this but at the moment not sure how.
Here is how to achieve what you want:
var propertyInfo = typeof(Data.Quote).GetProperty(sortName);
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
MemberExpression property = Expression.Property(parameter, propertyInfo);
LambdaExpression sort = Expression.Lambda(property, parameter);
MethodCallExpression call = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] {typeof(T), property.Type},
Query.Expression,
Expression.Quote(sort));
var orderedQuery = (IOrderedQueryable<T>)Query.Provider.CreateQuery<T>(call);
PaginatedList = orderedQuery.Skip(skipValue).Take(pageSize);
Instead of that you need to create an expression to select that property.From this source:
public static class Utility
{
//makes expression for specific prop
public static Expression<Func<TSource, object>> GetExpression<TSource>(string propertyName)
{
var param = Expression.Parameter(typeof(TSource), "x");
Expression conversion = Expression.Convert(Expression.Property
(param, propertyName), typeof(object)); //important to use the Expression.Convert
return Expression.Lambda<Func<TSource, object>>(conversion, param);
}
public static IOrderedQueryable<TSource>
OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
{
return source.OrderBy(GetExpression<TSource>(propertyName));
}
}
Then you can order by as I show below:
var result=Query.OrderBy(sortName)...;
The value being copied in to propertyInfo
is just an Object, as that is the type returned by GetProperty()
. Hover over var
and you will confirm this.
The GetValue
method doesn't exist for an Object, so you need to cast it to the right type before making the call to GetValue
.