Reputation: 1831
I need to know if an OrderBy was applied to a Linq query before applying an .Skip or .Take. I have no control of the received query and if an OrderBy was applied I need to maintain this one, in any other case I should OrderBy(t=>true). I have tried the following:
DataContext db;
var query = db.Orders;
var wasOrderByApplied = typeof(IOrderedQueryable<Order>).IsAssignableFrom(query.AsQueryable().Expression.Type);
var wasOrderByApplied2 = query.AsQueryable().Expression.Type == typeof(System.Data.Entity.Core.Objects.ObjectQuery<Order>);
var wasOrderByApplied3 = typeof(IOrderedQueryable<Order>) == query.AsQueryable().Expression.Type;
var query2 = db.Orders.OrderBy(o => o.CreationDate);
var wasOrderByApplied4 = typeof(IOrderedQueryable<Order>).IsAssignableFrom(query2.AsQueryable().Expression.Type);
var wasOrderByApplied5 = query2.AsQueryable().Expression.Type == typeof(System.Data.Entity.Core.Objects.ObjectQuery<Order>);
var wasOrderByApplied6 = typeof(IOrderedQueryable<Order>) == query2.AsQueryable().Expression.Type;
var query3 = db.Orders.OrderBy(o => o.CreationDate).Where(o => o.Id > 4);
var wasOrderByApplied7 = typeof(IOrderedQueryable<Order>).IsAssignableFrom(query3.AsQueryable().Expression.Type);
var wasOrderByApplied8 = query3.AsQueryable().Expression.Type == typeof(System.Data.Entity.Core.Objects.ObjectQuery<Order>);
var wasOrderByApplied9 = typeof(IOrderedQueryable<Order>) == query3.AsQueryable().Expression.Type;
The results where:
wasOrderByApplied = true;
wasOrderByApplied2 = true;
wasOrderByApplied3 = false;
wasOrderByApplied4 = true;
wasOrderByApplied5 = false;
wasOrderByApplied6 = true;
With the last results it seems that the third question asked to each query is the one that is correct, but then I did the third query (query3) and the results where:
wasOrderByApplied7 = false;
wasOrderByApplied8 = false;
wasOrderByApplied9 = false;
When I add a Where after the OrderBy the question result is false where it should be true.
Is there a better way to know if an OrderBy was applied to the query?
Upvotes: 5
Views: 1105
Reputation: 1980
You don't need to know what. It is pointless. If you want to Take
or Skip
some elements you need to order them just before that. Consider following
class Animal
{
public int Weight { get; set; }
public int NumberOfLegs { get; set; }
}
...
public IEnumerable<Animal> TakeFive(IEnumerable<Animal> animals)
{
return animals.Take(5);
}
What five animals would you take? Those what have most legs? Or those that weighting more? Or those that weighting less? You don't know. But if you change code like so
public IEnumerable<Animal> TakeFive(IEnumerable<Animal> animals)
{
return animals.OrderBy(_ => .Weight).Take(5);
}
It's starting to make some sense.
You can apply the same not only to IEnumerable
but to IQueryable
also. There is no need to know if sequence is ordered if you don't know what order is.
Upvotes: 0
Reputation: 27871
Here is one possible solution that came to my mind:
Create an expression visitor that checks whether the expression invokes the OrderBy
, or the OrderByDescending
method like this:
public class MyVisitor : ExpressionVisitor
{
public bool HasOrderBy { get; private set; }
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof (Queryable) &&
(node.Method.Name == "OrderBy" || node.Method.Name == "OrderByDescending"))
HasOrderBy = true;
return base.VisitMethodCall(node);
}
}
Here is how you can use it:
MyVisitor visitor = new MyVisitor();
visitor.Visit(query.Expression);
if (visitor.HasOrderBy)
{
//..
}
else
{
//..
}
Upvotes: 9