[linq] How can I do an OrderBy with a dynamic string parameter?

I want to do this:

var orderBy = "Nome, Cognome desc";

var timb = time.Timbratures.Include("Anagrafica_Dipendente")
    .Where(p => p.CodDipendente == 1);

if(orderBy != "")
    timb = timb.OrderBy(orderBy);

Is there an OrderBy overload available that accepts a string parameter?

This question is related to linq sql-order-by

The answer is


You need to use the LINQ Dynamic Query Library in order to pass parameters at runtime,

This will allow linq statements like

string orderedBy = "Description";
var query = (from p in products
            orderby(orderedBy)
            select p);

You don't need an external library for this. The below code works for LINQ to SQL/entities.

    /// <summary>
    /// Sorts the elements of a sequence according to a key and the sort order.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of <paramref name="query" />.</typeparam>
    /// <param name="query">A sequence of values to order.</param>
    /// <param name="key">Name of the property of <see cref="TSource"/> by which to sort the elements.</param>
    /// <param name="ascending">True for ascending order, false for descending order.</param>
    /// <returns>An <see cref="T:System.Linq.IOrderedQueryable`1" /> whose elements are sorted according to a key and sort order.</returns>
    public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> query, string key, bool ascending = true)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            return query;
        }

        var lambda = (dynamic)CreateExpression(typeof(TSource), key);

        return ascending 
            ? Queryable.OrderBy(query, lambda) 
            : Queryable.OrderByDescending(query, lambda);
    }

    private static LambdaExpression CreateExpression(Type type, string propertyName)
    {
        var param = Expression.Parameter(type, "x");

        Expression body = param;
        foreach (var member in propertyName.Split('.'))
        {
            body = Expression.PropertyOrField(body, member);
        }

        return Expression.Lambda(body, param);
    }

(CreateExpression copied from https://stackoverflow.com/a/16208620/111438)


Another solution from codeConcussion (https://stackoverflow.com/a/7265394/2793768)

var param = "Address";    
var pi = typeof(Student).GetProperty(param);    
var orderByAddress = items.OrderBy(x => pi.GetValue(x, null));

If you are using plain LINQ-to-objects and don't want to take a dependency on an external library it is not hard to achieve what you want.

The OrderBy() clause accepts a Func<TSource, TKey> that gets a sort key from a source element. You can define the function outside the OrderBy() clause:

Func<Item, Object> orderByFunc = null;

You can then assign it to different values depending on the sort criteria:

if (sortOrder == SortOrder.SortByName)
  orderByFunc = item => item.Name;
else if (sortOrder == SortOrder.SortByRank)
  orderByFunc = item => item.Rank;

Then you can sort:

var sortedItems = items.OrderBy(orderByFunc);

This example assumes that the source type is Item that have properties Name and Rank.

Note that in this example TKey is Object to not constrain the property types that can be sorted on. If the func returns a value type (like Int32) it will get boxed when sorting and that is somewhat inefficient. If you can constrain TKey to a specific value type you can work around this problem.


Look at this blog here. It describes a way to do this, by defining an EntitySorter<T>.

It allows you to pass in an IEntitySorter<T> into your service methods and use it like this:

public static Person[] GetAllPersons(IEntitySorter<Person> sorter)
{
    using (var db = ContextFactory.CreateContext())
    {
        IOrderedQueryable<Person> sortedList = sorter.Sort(db.Persons);

        return sortedList.ToArray();
    }
}

And you can create an EntitiySorter like this:

IEntitySorter<Person> sorter = EntitySorter<Person>
    .OrderBy(p => p.Name)
    .ThenByDescending(p => p.Id);

Or like this:

var sorter = EntitySorter<Person>
     .OrderByDescending("Address.City")
     .ThenBy("Id");

I did so:

using System.Linq.Expressions;

namespace System.Linq
{
    public static class LinqExtensions
    {

        public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string field, string dir = "asc")
        {
            // parametro => expressão
            var parametro = Expression.Parameter(typeof(TSource), "r");
            var expressao = Expression.Property(parametro, field);
            var lambda = Expression.Lambda(expressao, parametro); // r => r.AlgumaCoisa
            var tipo = typeof(TSource).GetProperty(field).PropertyType;

            var nome = "OrderBy";
            if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase))
            {
                nome = "OrderByDescending";
            }
            var metodo = typeof(Queryable).GetMethods().First(m => m.Name == nome && m.GetParameters().Length == 2);
            var metodoGenerico = metodo.MakeGenericMethod(new[] { typeof(TSource), tipo });
            return metodoGenerico.Invoke(source, new object[] { source, lambda }) as IOrderedQueryable<TSource>;

        }

        public static IOrderedQueryable<TSource> ThenBy<TSource>(this IOrderedQueryable<TSource> source, string field, string dir = "asc")
        {
            var parametro = Expression.Parameter(typeof(TSource), "r");
            var expressao = Expression.Property(parametro, field);
            var lambda = Expression.Lambda<Func<TSource, string>>(expressao, parametro); // r => r.AlgumaCoisa
            var tipo = typeof(TSource).GetProperty(field).PropertyType;

            var nome = "ThenBy";
            if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase))
            {
                nome = "ThenByDescending";
            }

            var metodo = typeof(Queryable).GetMethods().First(m => m.Name == nome && m.GetParameters().Length == 2);
            var metodoGenerico = metodo.MakeGenericMethod(new[] { typeof(TSource), tipo });
            return metodoGenerico.Invoke(source, new object[] { source, lambda }) as IOrderedQueryable<TSource>;
        }

    }
}

Use :

example.OrderBy("Nome", "desc").ThenBy("other")

Work like:

example.OrderByDescending(r => r.Nome).ThenBy(r => r.other)

In one answer above:

The simplest & the best solution:

mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s));

There is an syntax error, ,null must be added:

mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s,null));

The simplest & the best solution:

mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s));