ecasper ecasper - 1 month ago 5
C# Question

Combine Lambda Expressions for OrderBy

I'm trying to order an query using multiple columns. Following is my code and the parameter value. it fails at the line where I try to add two expressions with the error:


The binary operator Add is not defined for the types 'System.String'
and 'System.Single'.


Any help would be greatly appreciated!!

sortColumn = "table1.column1,table2.column1,column3"

public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string sortColumn, string direction) {
string methodName = string.Format("OrderBy{0}", direction.ToLower() == "asc" ? "" : "descending");
ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");
MemberExpression memberAccess = null;
LambdaExpression orderByLambda = null;

foreach (var fields in sortColumn.Split(',')) {
memberAccess = null;
foreach (var property in fields.Split('.')) {
memberAccess = MemberExpression.Property(memberAccess ?? (parameter as Expression), property);
} if (orderByLambda == null) {
orderByLambda = Expression.Lambda(memberAccess, parameter);
} else {
orderByLambda = Expression.Lambda(Expression.Add(orderByLambda.Body, Expression.Lambda(Expression.Invoke(Expression.Lambda(memberAccess, parameter), parameter), parameter).Body), parameter);
}
}
MethodCallExpression result = Expression.Call(
typeof(Queryable),
methodName,
new[] { query.ElementType, memberAccess.Type },
query.Expression,
Expression.Quote(orderByLambda));
return query.Provider.CreateQuery<T>(result);
}

Answer

You need to chain your expressions with OrderBy/ThenBy on the outer level, not on the inner level. Here is how:

public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string sortColumn, string direction) {
    var methodNameFirst = string.Format("OrderBy{0}", direction.ToLower() == "asc" ? "" : "descending");
    var methodNameContinue = string.Format("ThenBy{0}", direction.ToLower() == "asc" ? "" : "descending");
    ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");
    Expression result = query.Expression;
    var methodName = methodNameFirst;
    foreach (var fields in sortColumn.Split(',')) {
        Expression memberAccess = null;
        foreach (var property in fields.Split('.')) {
            memberAccess = MemberExpression.Property(memberAccess ?? (parameter as Expression), property);
        }
        LambdaExpression orderByLambda = Expression.Lambda(memberAccess, parameter);
        result = Expression.Call(
            typeof(Queryable),
            methodName,
            new[] { query.ElementType, memberAccess.Type },
            result,
            Expression.Quote(orderByLambda));
        methodName = methodNameContinue;
    }
    return query.Provider.CreateQuery<T>(result);
}