Generate sorted list using expression tree

Viewed 102

I have a collection which is of type IQueryable, I need to sort this based on some dynamic sort fields. Sort fields are inside a list.

I write the following method to do this.

public List<T> Order<T>(IQueryable<T> source, List<string> propertyNames)
    {

        if(propertyNames != null && propertyNames.Count > 0)
        {
            var param = Expression.Parameter(typeof(T), string.Empty);
            var property = Expression.PropertyOrField(param, propertyNames[0]);

            var sort = Expression.Lambda(property, param);
            MethodCallExpression orderByCall = Expression.Call(typeof(Queryable),"OrderBy",new[] { property.Type },Expression.Quote(sort));
            if(propertyNames.Count > 1)
            {
                foreach(var item in propertyNames)
                {
                    param = Expression.Parameter(typeof(T), string.Empty);
                    property = Expression.PropertyOrField(param, item);

                    sort = Expression.Lambda(property, param);

                    orderByCall = Expression.Call(
                        typeof(Queryable),
                        "ThenBy", new[] { typeof(T), property.Type },
                        orderByCall,
                        Expression.Quote(sort));
                }
            }

            var results = (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(orderByCall);
            if(results != null)
             return results.ToList();

        }

        return null;
    }

when I executed MethodCallExpression orderByCall = Expression.Call(typeof(Queryable),"OrderBy",new[] { property.Type },Expression.Quote(sort)); I got some exception

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.

1 Answers

Sorry, I don't have a direct solution for your error.

Here is an alternative ("kept it simple") approach to dynamically order your data.

1) Add these extension methods somewhere in your project

public static IOrderedQueryable<TSource> OrderBy<TSource, TProperty>(this IQueryable<TSource> source
      , Expression<Func<TSource, TProperty>> expression, bool descending)
{
      return !descending ? source.OrderBy(expression) : source.OrderByDescending(expression);
}

public static IOrderedQueryable<TSource> ThenBy<TSource, TProperty>(this IOrderedQueryable<TSource> source
  , Expression<Func<TSource, TProperty>> expression, bool descending)
{
  return !descending ? source.ThenBy(expression) : source.ThenByDescending(expression);
}

2) Now you can just loop your list of property names and apply the OrderBy / ThenBy on your IQueryable.

Other idea: you could adapt your method so it accepts expressions instead of property name strings.

Related