As the error message is telling you, you're passing a string
to OrderBy
wen it's expecting an Expression<Func<CompetencyAssessmentLineViewModel, TKey>>
. You need to provide such an expression.
Creating one is hard, because you don't even know the type of the field you are sorting on at compile time. This means that once you generate the proper expression for selecting the property, you have to use reflection to call OrderBy
simply because the generic arguments cannot be supplied at compile time.
private static Tuple<Expression, Type> GetSelector<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
Expression body = Expression.Property(parameter, propertyName);
return Tuple.Create(Expression.Lambda(body, parameter) as Expression
, body.Type);
}
private static IOrderedQueryable<T> OrderBy<T>(IQueryable<T> query,
string property, bool ascending)
{
var selector = GetSelector<T>(property);
Type[] argumentTypes = new[] { typeof(T), selector.Item2 };
var methodName = ascending ? "OrderBy" : "OrderByDescending";
var orderByMethod = typeof(Queryable).GetMethods()
.First(method => method.Name == methodName
&& method.GetParameters().Count() == 2)
.MakeGenericMethod(argumentTypes);
return (IOrderedQueryable<T>)
orderByMethod.Invoke(null, new object[] { query, selector.Item1 });
}
private static IOrderedQueryable<T> ThenBy<T>(IOrderedQueryable<T> query,
string property, bool ascending)
{
var selector = GetSelector<T>(property);
Type[] argumentTypes = new[] { typeof(T), selector.Item2 };
var methodName = ascending ? "ThenBy" : "ThenByDescending";
var orderByMethod = typeof(Queryable).GetMethods()
.First(method => method.Name == methodName
&& method.GetParameters().Count() == 2)
.MakeGenericMethod(argumentTypes);
return (IOrderedQueryable<T>)
orderByMethod.Invoke(null, new object[] { query, selector.Item1 });
}
public static IOrderedQueryable<T> OrderBy<T>(
this IQueryable<T> query,
string property)
{
return OrderBy<T>(query, property, true);
}
public static IOrderedQueryable<T> OrderByDescending<T>(
this IQueryable<T> query,
string property)
{
return OrderBy<T>(query, property, false);
}
public static IOrderedQueryable<T> ThenBy<T>(
this IOrderedQueryable<T> query,
string property)
{
return ThenBy<T>(query, property, true);
}
public static IOrderedQueryable<T> ThenByDescending<T>(
this IOrderedQueryable<T> query,
string property)
{
return ThenBy<T>(query, property, false);
}
Now that we have all of that in place to order based on a string property, you can do essentially what you wanted to do before:
var displayList = list.OrderBy(request.SortingName)
.ThenBy(request.SortingOrder)
.Skip(startIndex)
.Take(pageLength);