I found Marc Gravell's dynamic order by great:
Dynamic LINQ OrderBy on IEnumerable<T>
I've put it in a class, LinqHelper
. In this class I also have created two new classes, so that in my code I can do this:
var q = db.tblJobHeaders;
LinqHelper.OrderByCollection OBys = new LinqHelper.OrderByCollection();
OBys.AddOrderBy("some field", true);
OBys.AddOrderBy("anotherfield", false);
OBys.ExecuteOrderBys(q);
The classes to acheive this are:
/// <summary>
/// A collection of order bys
/// </summary>
public class OrderByCollection
{
private ArrayList Orderings = new ArrayList();
public OrderByCollection(){ }
/// <summary>
/// Add an order by to this collection
/// </summary>
public void AddOrderBy(string Field, bool Descending)
{
OrderByObj NewObj = new OrderByObj(Descending, Field);
this.Orderings.Add(NewObj);
}
/// <summary>
/// Executes the order bys
/// </summary>
public IOrderedQueryable<T> ExecuteOrderBys<T>(this IOrderedQueryable<T> source)
{
int ExecutionIndex = 0;
foreach (OrderByObj O in this.Orderings)
{
if (ExecutionIndex == 0)
{
if (O.Descending)
source = LinqHelper.OrderByDescending(source, O.Field);
else
source = LinqHelper.OrderBy(source, O.Field);
}
else
{
if (O.Descending)
source = LinqHelper.ThenByDescending(source, O.Field);
else
source = LinqHelper.ThenBy(source, O.Field);
}
ExecutionIndex++;
}
return (IOrderedQueryable<T>)source;
}
}
/// <summary>
/// An order by object
/// </summary>
private class OrderByObj
{
public bool Descending { get; set; }
public string Field { get; set; }
public OrderByObj(bool IsDescending, string DatabaseField)
{
this.Descending = IsDescending;
this.Field = DatabaseField;
}
}
Howver I'm pretty new to passing Linq vars through to functions (the confuses me a bit). I currently get the error on:
OBys.ExecuteOrderBys(q);
Which gives the error:
The type arguments for method 'LinqHelper.OrderByCollection.ExecuteOrderBys(System.Linq.IOrderedQueryable)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
I'm a bit confused about this if anyone could help, am I passing the var q
in properly, and then returning it properly?
I bet the type of q
is IQueryable<T>
and not IOrderedQueryable<T>
. Just changing the signature should work, because you start with OrderBy
.
Then you will need an IOrderedQueryable<T>
for the ThenBy
s. You can just cast it, because you know for sure that you have an IOrderedQueryable<T>
from the previous call to either OrderBy
or ThenBy
.
If you don't like the idea of the cast, you need some changes:
public IOrderedQueryable<T> ExecuteOrderBys<T>(this IQueryable<T> source)
{
if(!this.Orderings.Any())
throw new InvalidOperationException("You need to add orderings");
IOrderedQueryable<T> ordered;
if (this.Orderings[0].Descending)
ordered = LinqHelper.OrderByDescending(source, this.Orderings[0].Field);
else
ordered = LinqHelper.OrderBy(source, this.Orderings[0].Field);
foreach(var ordering in this.Orderings.Skip(1))
{
if (ordering.Descending)
ordered = LinqHelper.ThenByDescending(source, ordering.Field);
else
ordered = LinqHelper.ThenBy(source, ordering.Field);
}
return ordered;
}
Note your code will fail spectacularly if you don't add any orderings, because of the cast to IOrderedQueryable<T>
in the end. You could change the return type to IQueryable<T>
(which loses the ability to "attach" more OrderBys later), or throw if there are no orderings, like I did.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With