Search code examples
c#entity-framework-coresql-order-by

More than one OrderBy clause in EF Core per Paramter Func<T,obj> and how to build the query more generic


I have a base razor component, that loads data from a SQL database. To pass a sort order to this component, there are two "sort" Func<T,object> parameters and two bool to set asc/desc.

In my query this leads to the folloging code:

     if (sort1 != null && sort2 != null)
     {
     if (GridBehavior.SortOrderDescending1)
     {
         if (GridBehavior.SortOrderDescending2)
         {
             query = query.OrderByDescending(sort1).ThenByDescending(sort2);
         }
         else
         {
             query = query.OrderByDescending(sort1).ThenBy(sort2);
         }
     }
     else
     {
         if (GridBehavior.SortOrderDescending2)
         {
             query = query.OrderBy(sort1).ThenByDescending(sort2);
         }
         else
         {
             query = query.OrderBy(sort1).ThenBy(sort2);
         }
     }
 }
 else if (sort1 != null)
 {
     if (GridBehavior.SortOrderDescending1)
     {
         query = query.OrderByDescending(sort1);
     }
     else
     {
         query = query.OrderBy(sort1);
     }
 }

This looks very "ugly" to me at one hand, at the other I don't want to think how this looks if I have to extend this to a 3rd or even 4th order clause.

Can this be done more generic? I can't query with orderby() in sequence, because sorting will always be done on the whole set, so one overwrites the next.

Any hint will be greatly appreciated!

P.S.: The code will also not work, if only sort2 is specified!


Solution

  • You can put it into a list and then loop through the list.

    If you are using a database with EF Core then you should have Expressions not bare Func lambdas.

    List<(Expression<Func<T,object>> expression, bool descending)> sorts = [
        (sort1, GridBehavior.SortOrderDescending1),
        (sort2, GridBehavior.SortOrderDescending2)
    ];
    
    var sortedQuery = query.OrderBy(t => 1);
    
    foreach (var tuple in sorts)
    {
        if (tuple.expression != null)
        {
            if (tuple.descending)
                sortedQuery = sortedQuery.ThenByDescending(tuple.expression);
            else
                sortedQuery = sortedQuery.ThenBy(tuple.expression);
        }
    }
    

    The first .OrderBy(t => 1); will be optimized away by the database.