2

I wrote this code that sorts an IQueryable<T> by the column sortColumn. I would like to extend it so that the entries that have the value of the column BirthDate equal to DateTime.Today would be placed first in the sort, but I just can't find or think of how to do the job.

public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string sortColumn, bool asc)
{
    var param = Expression.Parameter(typeof(T), "p");

    var prop = Expression.Property(param, sortColumn);

    var exp = Expression.Lambda(prop, param);

    string method = asc ? "OrderBy" : "OrderByDescending";

    Type[] types = new[] { q.ElementType, exp.Body.Type };

    var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);

    return q.Provider.CreateQuery<T>(mce);
}
Simon MᶜKenzie
  • 7,576
  • 13
  • 43
  • 67
Catalin
  • 77
  • 1
  • 6
  • See: http://stackoverflow.com/questions/298725/multiple-order-by-in-linq – RBZ Sep 05 '12 at 14:50
  • That would work but then I could not use a parameter "sortColumn" to to sort for whatever column is needed unless I use lots of if(){}s. – Catalin Sep 05 '12 at 14:57
  • 1
    First, shouldn't you be returning an `IOrderedQueryable` here? Second, can't you create another copy of this that extends `IOrderedQueryable` instead of `IQueryable` that uses `ThenBy` and `ThenByDescending` as the `Queryable` method? – SPFiredrake Sep 05 '12 at 15:56

2 Answers2

8

See Handle GridView.OnSorting() and create sorting expression dynamically using LINQ

public static class SortExpressionBuilder<T>
{
    private static IDictionary<SortDirection, ISortExpression> directions =  new Dictionary<SortDirection, ISortExpression>
    {
        { SortDirection.Ascending, new OrderByAscendingSortExpression() },
        { SortDirection.Descending, new OrderByDescendingSortExpression() }
    };

    interface ISortExpression
    {
        Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression();
    }

    class OrderByAscendingSortExpression : ISortExpression
    {
        public Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression()
        {
            return (c, f) => c.OrderBy(f);
        }
    }

    class OrderByDescendingSortExpression : ISortExpression
    {
        public Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression()
        {
            return (c, f) => c.OrderByDescending(f);
        }
    }

    public static Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> CreateExpression(SortDirection direction)
    {
        return directions[direction].GetExpression();
    }
}

public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> collection, string columnName, SortDirection direction)
{
    ParameterExpression param = Expression.Parameter(typeof(T), "x"); // x
    Expression property = Expression.Property(param, columnName);     // x.ColumnName
    Func<T, object> func = Expression.Lambda<Func<T, object>>(        // x => x.ColumnName
       Expression.Convert(Expression.Property(param, columnName), 
       typeof(object)), param
    ).Compile();

    Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression = SortExpressionBuilder<T>.CreateExpression(direction);
    IEnumerable<T> sorted = expression(collection, func);
    return sorted;
}

And links from the bottom:

Community
  • 1
  • 1
abatishchev
  • 92,232
  • 78
  • 284
  • 421
3

You can try to sort on something similar to the following expression:

.OrderBy(x => BirthDate == DateTime.Now.Date ? 0 : 1).ThenBy(sortColumn);
kerem
  • 2,639
  • 1
  • 29
  • 37