views:

51

answers:

3

I have a query of IQueryable and want to apply sorting to it dynamically, sorting can be on many columns (asc or desc). I've written the following generic function:

    private IQueryable<T> ApplySorting<T,U>(IQueryable<T> query, Expression<Func<T, U>> predicate, SortOrder order)
    {
        if (order == SortOrder.Ascending)
        {
            {
                return query.OrderBy<T, U>(predicate);
            }
        }
        else
        {
            {
                return query.OrderByDescending<T, U>(predicate);
            }
        }
    }

SortOrder is my simple enum with 2 values: Ascending and Descending

Then I call this function in a loop, for each column that user requested sorting. However I've noticed it fails because it always sorts on the last column used, ignoring the other ones.

Then I found there's a 'ThenBy' method on IOrderedQueryable so the valid usage is:

var q = db.MyType.OrderBy(x=>x.Col1).ThenBy(y=>y.Col2); //etc.

But how can I make it generic? I tried to test if the query is IOrderedQueryable but it seems always to be true even if it's simplest var q = from x in db.MyType select x

I have no clue why it was designed like this. What's wrong with:

var q = db.MyType.OrderBy(x=>x.Col1).OrderBy(y=>y.Col2); //etc.

it's so much intuitive

A: 

Total guess, but can you do something like this?

query.OrderBy(x => 1).ThenBy<T,U>(predicate)

Any syntax errors aside, the idea is to do an OrderBy() that doesn't affect anything, then do the real work in the .ThenBy() method call

Ian Jacobs
Nice hack, and it should do the job, but still it's only a hack. It's hard to believe that linq authors did not think of such scenario. With python/django doing such things is so trivial...
PawelRoman
+1  A: 

You just need to check if the query is already ordered :

private IQueryable<T> ApplySorting<T,U>(IQueryable<T> query, Expression<Func<T, U>> predicate, SortOrder order)
{
    var ordered = query as IOrderedQueryable<T>;
    if (order == SortOrder.Ascending)
    {
        if (ordered != null)
            return ordered.ThenBy(predicate);
        return query.OrderBy(predicate);
    }
    else
    {
        if (ordered != null)
            return ordered.ThenByDescending(predicate);
        return query.OrderByDescending(predicate);
    }
}
Thomas Levesque
This doesnt work. Like I said, query ALWAYS is of type IOrderedQueryable, event if it's a simplest from x in y select x;
PawelRoman
A: 

I would write a wrapper around and internally use linq extension methods.

var resultList =   presentList
        .MyOrderBy(x => x.Something)
        .MyOrderBY(y => y.SomethingElse)
        .MyOrderByDesc(z => z.AnotherThing)

public IQueryable<T> MyOrderBy(IQueryable<T> prevList, Expression<Func<T, U>> predicate) {

       return (prevList is IOrderedQueryable<T>)
            ? query.ThenBy(predicate)
            : query.OrderBy(predicate);
}

public IQueryable<T> MyOrderByDesc(IQueryable<T> prevList, Expression<Func<T, U>> predicate) {

       return (prevList is IOrderedQueryable<T>)
            ? query.ThenByDescending(predicate)
            : query.OrderByDescending(predicate);
}

PS: I didn't test the code

Mahesh Velaga
If you test this code you will find that IQueryable<T> is always IOrderedQueryable<T>, so ThenBy function will always called and OrderBy will never be called. This is similar to how I tried to implement it before writing here and found this surprise.
PawelRoman