For example, I want to try something like this. The sorting par might have none, 1 or several sorting columns by different order. But I can't use the ThenBy
method as it's only available after an OrderBy
. The code below will keep resetting the order to the last item in the sorting list. I don't want to change the method signature either. Help would be greatly appreciated, thanks.
public IQueryable<Person> FilterList(string forename, List<Sorting> sorting)
{
IQueryable<Person> query = dc.Set<Person>();
if(!string.IsNullOrEmpty(forename)){
query = query.Where(w=>w.Forename.Contains(forename));
foreach(var sort in sorting)
{
switch(sort.By)
{
case "asc":
switch(sort.Sort)
{
case "forename":
query = query.OrderBy(o=>o.Forename);
break;
case "surname":
query = query.OrderBy(o=>o.Surname);
break;
}
break;
case "desc":
switch(sort.Sort)
{
case "forename":
query = query.OrderByDescending(o=>o.Forename);
break;
case "surname":
query = query.OrderByDescending(o=>o.Surname);
break;
}
break;
}
}
return query;
}
public class Sorting
{
public string Sort{get;set;}
public string By{get;set;}
}
AgentFire's answer is effectively appropriate, but a bit long-winded to special-case in each situation. I'd add an extension method:
EDIT: Apparently, the code below doesn't correctly determine whether the query is already ordered; even the unordered version implements IOrderedQueryable<T>
, apparently. Using source.Expression.Type
to check for IOrderedQueryable<T>
apparently works, but I've left this code here as the more logical approach - and as this "fix" sounds more brittle than I'd like for an answer with my name on it :)
public static IOrderedQueryable<T> AddOrdering<T, TKey>(
this IQueryable<T> source,
Expression<Func<T, TKey>> keySelector,
bool descending)
{
IOrderedQueryable<T> ordered = source as IOrderedQueryable<T>;
// If it's not ordered yet, use OrderBy/OrderByDescending.
if (ordered == null)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
// Already ordered, so use ThenBy/ThenByDescending
return descending ? ordered.ThenByDescending(keySelector)
: ordered.ThenBy(keySelector);
}
Then your calling code becomes:
foreach(var sort in sorting)
{
bool descending = sort.By == "desc";
switch (sort.Sort)
{
case "forename":
query = query.AddOrdering(o => o.Forename, descending);
break;
case "surname":
query = query.AddOrdering(o => o.Surname, descending);
break;
}
}
This way each additional sort option only adds a tiny code overhead.
See more on this question at Stackoverflow