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 eac开发者_JS百科h 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
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);
}
}
How about just making the first OrderBy
static, then always ThenBy
?
OrderColumn[] columnsToOrderby = getColumnsToOrderby();
IQueryable<T> data = getData();
if(!columnToOrderBy.Any()) { }
else
{
OrderColumn firstColumn = columnsToOrderBy[0];
IOrderedEnumerable<T> orderedData =
firstColumn.Ascending
? data.OrderBy(predicate)
: data.OrderByDescending(predicate);
for (int i = 1; i < columnsToOrderBy.Length; i++)
{
OrderColumn column = columnsToOrderBy[i];
orderedData =
column.Ascending
? orderedData.ThenBy(predicate)
: orderedData.ThenByDescending(predicate);
}
}
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
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
Extension to dynamic multi-order:
public static class DynamicExtentions
{
public static IEnumerable<T> DynamicOrder<T>(this IEnumerable<T> data, string[] orderings) where T : class
{
var orderedData = data.OrderBy(x => x.GetPropertyDynamic(orderings.First()));
foreach (var nextOrder in orderings.Skip(1))
{
orderedData = orderedData.ThenBy(x => x.GetPropertyDynamic(nextOrder));
}
return orderedData;
}
public static object GetPropertyDynamic<Tobj>(this Tobj self, string propertyName) where Tobj : class
{
var param = Expression.Parameter(typeof(Tobj), "value");
var getter = Expression.Property(param, propertyName);
var boxer = Expression.TypeAs(getter, typeof(object));
var getPropValue = Expression.Lambda<Func<Tobj, object>>(boxer, param).Compile();
return getPropValue(self);
}
}
Example:
var q =(myItemsToSort.Order(["Col1","Col2"]);
Note: not sure any IQueryable
provider can translate this
精彩评论