9

I have a query where I need to have ordeby based on a querystring parameter .For example if sortby parameter is price , Query needs to change with price . If its rating than change query to sort by rating .

I know PredicateBuilder can do And and OR stuff but how do I make a dynamic ordeby linq query .

Brian Webster
  • 27,545
  • 47
  • 143
  • 218
Pit Digger
  • 8,992
  • 23
  • 70
  • 120
  • possible duplicate of [Dynamic LINQ OrderBy](http://stackoverflow.com/questions/41244/dynamic-linq-orderby) – Kirk Woll Oct 21 '11 at 22:47

3 Answers3

22

If you know exactly which are all the posible parameters that can be used to order, the Jon´s answer is the best one. But if you have an unknown number of parameters you can build the expression dynamically. e.g:

class Program
{
    static void Main(string[] args)
    {
        var people = new[]{
            new Person { Name = "David", Age = 40 },
            new Person { Name = "Maria", Age = 12 },
            new Person { Name = "Lucas", Age = 45 }
        }.AsQueryable();

        foreach (var p in people.OrderBy("Age"))
        {
            Console.Write(p.Name);
        }
    }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

static class IQueryableExtensions
{
    public static IQueryable<T> OrderBy<T>(this IQueryable<T> items, string propertyName)
    {
        var typeOfT = typeof(T);
        var parameter = Expression.Parameter(typeOfT, "parameter");
        var propertyType = typeOfT.GetProperty(propertyName).PropertyType;
        var propertyAccess = Expression.PropertyOrField(parameter, propertyName);
        var orderExpression = Expression.Lambda(propertyAccess, parameter);

        var expression = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { typeOfT, propertyType }, items.Expression, Expression.Quote(orderExpression));
        return items.Provider.CreateQuery<T>(expression);
    }        
}
lontivero
  • 5,011
  • 5
  • 23
  • 40
15

Well, you could use a switch statement or something similar:

IQueryable<Foo> query = ...;

switch (orderByParameter)
{
    case "price":
        query = query.OrderBy(x => x.Price);
        break;
    case "rating":
        query = query.OrderBy(x => x.Rating);
        break;
    // etc
}

You could also do it with reflection, but assuming you have a limited number of fields to order by, this is quite possibly the simplest approach.

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
3

Expanding on @lontivero answer.

If you want to do dynamic sorting of multiple items both ascending and descending you can do something similar to this below. Adds the OrderByDescending, ThenBy, ThenByDescending methods

static class IQueryableExtensions
 {
     public static IQueryable<T> OrderBy<T>(this IQueryable<T> items, string propertyName)
     {
         var typeOfT = typeof(T);
         var parameter = Expression.Parameter(typeOfT, "parameter");
         var propertyType = typeOfT.GetProperty(propertyName).PropertyType;
         var propertyAccess = Expression.PropertyOrField(parameter, propertyName);
         var orderExpression = Expression.Lambda(propertyAccess, parameter);

         var expression = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { typeOfT, propertyType }, items.Expression, Expression.Quote(orderExpression));
         return items.Provider.CreateQuery<T>(expression);
     }

     public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> items, string propertyName)
     {
         var typeOfT = typeof(T);
         var parameter = Expression.Parameter(typeOfT, "parameter");
         var propertyType = typeOfT.GetProperty(propertyName).PropertyType;
         var propertyAccess = Expression.PropertyOrField(parameter, propertyName);
         var orderExpression = Expression.Lambda(propertyAccess, parameter);

         var expression = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { typeOfT, propertyType }, items.Expression, Expression.Quote(orderExpression));
         return items.Provider.CreateQuery<T>(expression);
     }

     public static IQueryable<T> ThenBy<T>(this IQueryable<T> items, string propertyName)
     {
         var typeOfT = typeof(T);
         var parameter = Expression.Parameter(typeOfT, "parameter");
         var propertyType = typeOfT.GetProperty(propertyName).PropertyType;
         var propertyAccess = Expression.PropertyOrField(parameter, propertyName);
         var orderExpression = Expression.Lambda(propertyAccess, parameter);

         var expression = Expression.Call(typeof(Queryable), "ThenBy", new Type[] { typeOfT, propertyType }, items.Expression, Expression.Quote(orderExpression));
         return items.Provider.CreateQuery<T>(expression);
     }

     public static IQueryable<T> ThenByDescending<T>(this IQueryable<T> items, string propertyName)
     {
         var typeOfT = typeof(T);
         var parameter = Expression.Parameter(typeOfT, "parameter");
         var propertyType = typeOfT.GetProperty(propertyName).PropertyType;
         var propertyAccess = Expression.PropertyOrField(parameter, propertyName);
         var orderExpression = Expression.Lambda(propertyAccess, parameter);

         var expression = Expression.Call(typeof(Queryable), "ThenByDescending", new Type[] { typeOfT, propertyType }, items.Expression, Expression.Quote(orderExpression));
         return items.Provider.CreateQuery<T>(expression);
     }
 }
hewstone
  • 3,595
  • 2
  • 19
  • 24