In a generic abstract base class I'm storing a couple of expressions used for ordering:
public Expression<Func<T, string>> OrderByString { get; set; }
public Expression<Func<T, int>> OrderByInt { get; set; }
The get used later on in the generic base class:
if (OrderByString != null)
{
results = results.OrderBy(OrderByString);
}
else if (OrderByInt != null)
{
results = results.OrderBy(OrderByInt);
}
Finally one of them will get set in the deriving concrete class's constructer:
this.OrderByString = c => c.CustomerID;
I don't like the fact that I need to have separate expressions based on the property type I want to OrderBy. ToString won't work on the property's because LINQ to Entities doesn't support it. What I'm after is a way of storing an expression that picks any of the properties to order on regardless of type.
If I try something a little more generic such as:
public Expression<Func<T, object>> Order { get; set; }
Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types.
Additionally if I try a slight hack this also doesn't work:
public Expression<Func<T, string>> Order { get; set; }
this.Order = c => c.OrderID.ToString();
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
Sounds like you want a way to pile up a bunch of Ordering in a list somewhere and apply it. But you can't because each Expression has its own type, which is checked by the compiler when calling OrderBy. You must have those two types when calling OrderBy, but you must have one type to put in the same list.
Hide that second type behind an interface.
public interface IOrderer<T>
{
IOrderedQueryable<T> ApplyOrderBy(IQueryable<T> source);
IOrderedQueryable<T> ApplyOrderByDescending(IQueryable<T> source);
IOrderedQueryable<T> ApplyThenBy(IOrderedQueryable<T> source);
IOrderedQueryable<T> ApplyThenByDescending(IOrderedQueryable<T> source);
}
public class Orderer<T, U> : IOrderer<T>
{
private Expression<Func<T, U>> _orderExpr;
public Orderer(Expression<Func<T, U>> orderExpr) { _orderExpr = orderExpr; }
public IOrderedQueryable<T> ApplyOrderBy(IQueryable<T> source)
{ return source.OrderBy(_orderExpr); }
public IOrderedQueryable<T> ApplyOrderByDescending(IQueryable<T> source)
{ return source.OrderByDescending(_orderExpr); }
public IOrderedQueryable<T> ApplyThenBy(IOrderedQueryable<T> source)
{ return source.ThenBy(_orderExpr); }
public IOrderedQueryable<T> ApplyThenByDescending(IOrderedQueryable<T> source)
{ return source.ThenByDescending(_orderExpr); }
}
public class OrderCoordinator<T>
{
public List<IOrderer<T>> Orders { get; set; }
public OrderCoordinator() { Orders = new List<IOrderer<T>>(); }
//note, did not return IOrderedQueryable to support ability to return with empty Orders
public IQueryable<T> ApplyOrders(IQueryable<T> source)
{
foreach (IOrderer<T> orderer in Orders)
{
source = orderer.ApplyOrderBy(source);
}
return source;
}
}
public class Customer
{
public string Name { get; set; }
public int FavNumber { get; set; }
}
public class Tester
{
public void Test()
{
OrderCoordinator<Customer> coord = new OrderCoordinator<Customer>();
coord.Orders.Add(new Orderer<Customer, string>(c => c.Name));
coord.Orders.Add(new Orderer<Customer, int>(c => c.FavNumber));
IQueryable<Customer> query = Enumerable.Empty<Customer>().AsQueryable();
query = coord.ApplyOrders(query);
string result = query.Expression.ToString();
}
}
In the debugger:
result = "OrderingDemo.Customer[].OrderBy(c => c.Name).OrderBy(c => c.FavNumber)"
So in your case, instead of this property:
public Expression<Func<T, U>> Order { get; set; }
use this property
public IOrderer<T> Order { get; set; }
This is easy to do if you use the DynamicLinq library found at NuGet.org. This allows you to write queries like;
db.People.Where("Id == 8");
db.People.OrderBy("Created ASC");
That way you can save or pass in your where clauses as strings. No fuss, no muss.
http://nuget.org/List/Packages/DynamicLINQ
Amy B's answer is great, and I based my own solution on it. So my point is more of an improvement for what I needed, which I am likely to improve upon in time.
public interface IOrderer<TItem>
{
IOrderedQueryable<TItem> Apply(IQueryable<TItem> source);
}
public class OrderBy<TItem, TType> : IOrderer<TItem>
{
private Expression<Func<TItem, TType>> _orderExpr;
public OrderBy(Expression<Func<TItem, TType>> orderExpr)
{
_orderExpr = orderExpr;
}
public IOrderedQueryable<TItem> Apply(IQueryable<TItem> source)
{
return source.OrderBy(_orderExpr);
}
}
public class ThenBy<TItem, TType> : IOrderer<TItem>
{
private Expression<Func<TItem, TType>> _orderExpr;
public ThenBy(Expression<Func<TItem, TType>> orderExpr)
{
_orderExpr = orderExpr;
}
public IOrderedQueryable<TItem> Apply(IQueryable<TItem> source)
{
return ((IOrderedQueryable<TItem>)source).ThenBy(_orderExpr);
}
}
public class OrderCoordinator<TItem>
{
public List<IOrderer<TItem>> Orders { get; private set; } = new List<IOrderer<TItem>>();
public IQueryable<TItem> ApplyOrder(IQueryable<TItem> source)
{
foreach (IOrderer<TItem> orderer in Orders)
{
source = orderer.Apply(source);
}
return source;
}
public OrderCoordinator<TItem> OrderBy<TValueType>(Expression<Func<TItem, TValueType>> orderByExpression)
{
Orders.Add(new OrderBy<TItem, TValueType>(orderByExpression));
return this;
}
// Can add more sort calls over time
public OrderCoordinator<TItem> ThenBy<TValueType>(Expression<Func<TItem, TValueType>> orderByExpression)
{
Orders.Add(new ThenBy<TItem, TValueType>(orderByExpression));
return this;
}
}
Specify Coordinator with type:
public OrderCoordinator<MyObjectType> OrderCoordinator { get; private set; } = new OrderCoordinator<MyObjectType>();
Specify sort ordering:
OrderCoordinator.OrderBy(e => e.MyStringProperty).ThenBy(e => e.MyIntProperty);
Apply the ordering:
ordered = OrderCoordinator.ApplyOrder(ordered);
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