Search code examples
c#dynamicexpression-treeslinq-expressions

How to create expression for ordering string by length


I am new to expression tree. I need to order my collection base on string length and then string. I dont want to repeat my code for every entity. Any body know how to write it with expression tree?

somthing like this:

public static IQueryable<T> OrderByPropertyOrField<T>(this IQueryable<T> queryable,
  string propertyOrFieldName, bool ascending = true)
{
    var elementType = typeof(T);
    var orderByMethodName = ascending ? "OrderBy" : "OrderByDescending";

    var parameterExpression = Expression.Parameter(elementType);
    var propertyOrFieldExpression = Expression.PropertyOrField(parameterExpression, propertyOrFieldName);
    var selector = Expression.Lambda(propertyOrFieldExpression, parameterExpression);

    var orderByExpression = Expression.Call(typeof(Queryable), orderByMethodName,
        new[] { elementType, propertyOrFieldExpression.Type }, queryable.Expression, selector);

    return queryable.Provider.CreateQuery<T>(orderByExpression);
}

Solution

  • If you need to order by string value after ordering by string length you need to invoke another method of queryable - ThenBy or ThenByDescending depending on ascending value. Next code will create that ordering:

    public static class QueryableExtensions
    {
        public static IQueryable<T> OrderByPropertyOrField<T>(this IQueryable<T> queryable,
          string propertyOrFieldName, bool ascending = true)
        {
            var parameter = Expression.Parameter(queryable.ElementType, "x");
            var selector = Expression.PropertyOrField(parameter, propertyOrFieldName);
    
            var getLength = Expression.PropertyOrField(selector, "Length");
            var orderByLength = CreateOrderExpression(parameter,
              typeof(int),
              queryable.Expression, // order source collection
              getLength,
              ascending ? "OrderBy" : "OrderByDescending");
    
            var orderByValue = CreateOrderExpression(parameter,
              typeof(string),
              orderByLength, // order previous collection
              selector,
              ascending ? "ThenBy" : "ThenByDescending");
    
            return queryable.Provider.CreateQuery<T>(orderByValue);
        }
    
        private static Expression CreateOrderExpression(ParameterExpression parameter, Type keyType, Expression collection, Expression selector, string methodName)
        {
            return Expression.Call(
                typeof(Queryable),
                methodName,
                new[] { parameter.Type, keyType },
                collection,
                Expression.Lambda(selector, parameter)
            );
        }
    }