1

I am building a repository and would like to be able to pass a list of sorts to a function to receive the entities back in the correct order and possibly paged.

I found out that linq will correctly sort if you use reverse the list of sorts and use Orderby for each sort expression..

        foreach (var s in sorts.Reverse())
        {
            query = query.OrderBy(s);
        }

However while testing the addition of the sort direction I found that it only takes the first sort direction and seems to apply that direction on each sort.

    private void applySorts(ref IQueryable<TEntity> query, Dictionary<Expression<Func<TEntity, dynamic>>, string> sorts)
    {
        if (sorts != null)
        {
            foreach (var s in sorts.Reverse())
            {
                Expression<Func<TEntity, dynamic>> expr = s.Key;
                string dir = s.Value;
                if (dir == "d")
                {
                    query = query.OrderByDescending(expr);
                }
                else
                {
                    query = query.OrderBy(expr);
                }
            }
        }
    }

I originally tried to use OrderBy on the first sort and then switch to ThenBy, but this is not possible because ThenBy requires a type of IOrderedQueryable.

I would like to note the use of dictionary may not be the best, if you have any better ideas please share. However, I just wanted to get it running and see how things go.

I am using C#, Linq, and Entity Framework.

Thanks in advance for your time.

Update: Unfortunately I have found that this does not support sorting numbers. Error(Unable to cast the type 'System.Int32' to type 'System.Object'.)

Nato
  • 321
  • 2
  • 4
  • I would use a SortedDictionary with the key being a custom class that inherits IComparable which implements the method CompareTo(). See posting : http://stackoverflow.com/questions/4188013/c-sharp-interfaces-how-to-implement-icomparable – jdweng Jun 24 '16 at 23:55

2 Answers2

1

Grammatically, all you need is a temp variable of type IOrderedQueryable.

I would try something like:

    private void applySorts(ref IQueryable<TEntity> query, Dictionary<Expression<Func<TEntity, dynamic>>, string> sorts)
{
    if (sorts != null)
    {
        IOrderedQueryable<TEntity> tempQuery = null;
        bool isFirst = true;
        foreach (var s in sorts.Reverse())
        {
            Expression<Func<TEntity, dynamic>> expr = s.Key;
            string dir = s.Value;
            if (first) 
            {
                first = false;
                if (dir == "d")
                {
                    tempQuery = query.OrderByDescending(expr);
                }
                else
                {
                    tempQuery = query.OrderBy(expr);
                }
            }
            else 
            {   
                if (dir == "d")
                {
                    tempQuery = tempQuery.ThenByDescending(expr);
                }
                else
                {
                    tempQuery = tempQuery.ThenBy(expr);
                }
            }
        }
        query = tempQuery;
    }
}

Edit:

The key to the above solution is that an IOrderedQueryable is an IQueryable.

I haven't tried it myself. However, I would emphasis that this is only a grammatical solution.

Shane Lu
  • 1,056
  • 1
  • 12
  • 21
0

You can use ThenBy() method to chain your filters like this:

bool first = true;

foreach(var s in sorts) 
{
    query = first ? query.OrderBy(s) : query.ThenBy(s);
}
// It will became like query.OrderBy(a => a.A).ThenBy(a => a.B).ThenBy(a => a.C)...

You can also use sorting by multiple columns in a LINQ query if you know the properties on which you will sort:

var result = (from row in list
              orderby row.A, row.B, row.C
              select row).ToList();
Ivan Yurchenko
  • 3,762
  • 1
  • 21
  • 35
  • `var query = query.OrderBy(sorts[0]);` won't compile. –  Jun 24 '16 at 23:09
  • @hvd Ok I didn’t notice that it’s a dictionary. :) Then he can just change the loop to `foreach` instead of `for` the way as he did. Just apply `OrderBy()` to the first sorting expression and `ThenBy()` to all other. – Ivan Yurchenko Jun 24 '16 at 23:13
  • Heh, I actually meant you can't have a local variable with the same name as the parameter, but yeah, that too. :) With your edited version, `query.ThenBy` won't compile, `query` has type `IQueryable`. You were closer with your first version, you do need a separate variable, you just need to give it a different name. –  Jun 24 '16 at 23:23