Justin
Justin

Reputation: 18186

'No generic method > 'OrderByDescending' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments

I'm trying to dynamically use reflection to do an OrderBy on whether a given sortColumn (string) is null or not, a ThenBy on the sortColumn value, and a ThenBy on hard-coded columns. Like so:

if (!String.IsNullOrEmpty(sortColumn)) {
    var descending = sortDirection == "desc";
    views = views.AsQueryable()
        .OrderByNull<ToDoView>(sortColumn, true)  // extension method
        .OrderBy<ToDoView>(sortColumn, descending, true)  // extension method
        .ThenBy(v => v.summary ?? v.description).ToList();
}

Using other SO answers, I was able to get the OrderBy extension method to work:

public static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, 
        string orderByProperty, bool desc, bool thenBy = false) {
    string command = desc ? "OrderByDescending" : "OrderBy";
    if (thenBy)
        command = desc ? "ThenByDescending" : "ThenBy";
    var type = typeof(TEntity);
    var property = type.GetProperty(orderByProperty);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExpression = Expression.Lambda(propertyAccess, parameter);
    var resultExpression =  Expression.Call(
        typeof(Queryable), 
        command, 
        new Type[] { type, property.PropertyType },
        source.Expression, 
        Expression.Quote(orderByExpression));
    return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery<TEntity>(resultExpression);
}

Here is my OrderByNull extension method:

public static IOrderedQueryable<TEntity> OrderByNull<TEntity>(this IQueryable<TEntity> source, 
        string orderByProperty, bool desc, bool thenBy = false) {
    string command = desc ? "OrderByDescending" : "OrderBy";
    if (thenBy)
        command = desc ? "ThenByDescending" : "ThenBy";
    var type = typeof(TEntity);
    var property = type.GetProperty(orderByProperty);
    var parameter = Expression.Parameter(type, "p");
    var target = Expression.Constant(null, type);
    var equalsMethod = Expression.Call(Expression.Property(parameter, orderByProperty), "Equals", null, target);
    var orderByExpression = Expression.Lambda(equalsMethod, parameter);
    var resultExpression = Expression.Call(
        typeof(Queryable), 
        command, 
        new Type[] { type, property.PropertyType },
        source.Expression, 
        Expression.Quote(orderByExpression));
    return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery<TEntity>(resultExpression);
}

When executing this extension method, the orderByExpression evaluates to:

{p => p.listName.Equals(null)}

However, when trying to set the resultExpression, it throws an exception:

System.InvalidOperationException: 'No generic method 'OrderByDescending' 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.'

I'm not really sure how to fix this, as the expression looks right to me. Any ideas?


Here is my final OrderByNull extension method, after the fix from the accepted answer and resolving a NullReferenceException with the equals logic:

public static IOrderedQueryable<TEntity> OrderByNull<TEntity>(this IQueryable<TEntity> source, 
        string orderByProperty, bool desc, bool thenBy = false) {
    string command = desc ? "OrderByDescending" : "OrderBy";
    if (thenBy)
        command = desc ? "ThenByDescending" : "ThenBy";
    var type = typeof(TEntity);
    var property = type.GetProperty(orderByProperty);
    var parameter = Expression.Parameter(type, "p");
    var target = Expression.Constant(null, type);
    var equalsMethod = Expression.Equal(Expression.Property(parameter, orderByProperty), Expression.Constant(null, typeof(object)));
    var orderByExpression = Expression.Lambda(equalsMethod, parameter);
    var resultExpression = Expression.Call(
        typeof(Queryable), 
        command, 
        new Type[] { type, typeof(bool) },
        source.Expression, 
        Expression.Quote(orderByExpression));
    return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery<TEntity>(resultExpression);
}

Upvotes: 3

Views: 2635

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205729

In the failing case you are specifying property.PropertyType (new Type[] { type, property.PropertyType }) as a TKey generic type argument of the called generic method OrderBy<TSource, TKey>, but the selector lambda result type (which should match the TKey) is bool, hence the exception.

To fix it, change

new Type[] { type, property.PropertyType }

to

new Type[] { type, typeof(bool) }

or more generically (can be used in both methods)

new Type[] { type, orderByExpression.ReturnType  }

Upvotes: 7

Related Questions