'Determine whether an IQueryable<T> has been ordered or not
Is there a way to know if an IQueryable<T>
has been ordered (using OrderBy
or OrderbyDescending
)?
So I know whether to call OrderBy
or ThenBy
on the collection.
IQueryable<Contact> contacts = Database.GetContacts();
I tried contacts is IOrderedQueryable<Contact>
, but it's always true.
Edit: I just changed my example, the previous one wasn't really showing my point. Assume that GetContacts
uses Entity Framework and simply returns all the records of a table.
Later on, I apply several functions to contacts
, I have no knowledge of what those functions do. They can sort or filter the IQueryable<Contact>
.
When I get the collection back, I need to sort it once more. To do so, I need to know whether I need to call OrderBy
, or ThenBy
. So I don't reorder the whole collection if it has already been sorted.
Solution 1:[1]
Short answer is no, the Queryable class doesn't maintain a flag or whether the collection is sorted nor what method may have been used to perform such a sort.
http://msdn.microsoft.com/en-us/library/system.linq.queryable.aspx
Solution 2:[2]
It's possible. Here's an extension method:
public static bool IsOrdered<T>(this IQueryable<T> queryable)
{
if (queryable == null)
{
throw new ArgumentNullException(nameof(queryable));
}
return queryable.Expression.Type == typeof(IOrderedQueryable<T>);
}
Solution 3:[3]
Yes you can inspect the IQueryable.Expression
tree to see if it calls any of the OrderBy/ThenBy
methods. Expression trees can be examined by deriving a class from ExpressionVisitor.
There is an internal OrderingMethodFinder
in System.Web
- which you could adapt. Here's what I came up with:
// Adapted from internal System.Web.Util.OrderingMethodFinder http://referencesource.microsoft.com/#System.Web/Util/OrderingMethodFinder.cs
class OrderingMethodFinder : ExpressionVisitor
{
bool _orderingMethodFound = false;
protected override Expression VisitMethodCall(MethodCallExpression node)
{
var name = node.Method.Name;
if (node.Method.DeclaringType == typeof(Queryable) && (
name.StartsWith("OrderBy", StringComparison.Ordinal) ||
name.StartsWith("ThenBy", StringComparison.Ordinal)))
{
_orderingMethodFound = true;
}
return base.VisitMethodCall(node);
}
public static bool OrderMethodExists(Expression expression)
{
var visitor = new OrderingMethodFinder();
visitor.Visit(expression);
return visitor._orderingMethodFound;
}
}
Use it like so:
bool isOrdered = OrderingMethodFinder.OrderMethodExists(myQuery.Expression);
Solution 4:[4]
Actually, you can.
First problem I spot in your code is that you're casting the collection to IQueryable
without any reason to do so.
The following snippet:
var numbers = new[] {1, 5, 6, 87, 3};
Console.Write(numbers is IOrderedEnumerable<int>);
var ordered = numbers.OrderBy(c => c);
Console.Write(ordered is IOrderedEnumerable<int>);
Doesn't even need to be run: the first check gets you a design time warning saying that this expression will never be true.
Anyway, if you run it, it will give you False for first check, and True for the second check.
You can do the same thing with IQueryable<T>
and IOrderedQueryable<T>
providing you're really using that type, and not casting a collection to it.
Solution 5:[5]
You aren't ever going to know if the objects have been ordered properly, unless you check the ordering yourself. Your example is easy to see they aren't ordered, because numbers have a natural order, but IQueryable is a generic, which means that it can handle different types of objects. The ordering of say user objects (FirstName, LastName, DateStart, and LastPayDate) has an arbitrary order, and so the order they are returned in is not necessarily the order you are looking for. (Which is considered the primary field for the sort? It depends on your need.) So in theory, the question, "Are they ordered" could always be "Yes!" The order you are looking for might be wildly different than what the system returns.
Solution 6:[6]
You can examine the ToString() of your query to find out if Order By is used.
When a join occurs IQueryable's ToString puts parantheses to the begininng and end of the inner query. So if you find the last closing parantheses you can check if your outer most query has an Order By clause.
private bool isOrdered(IQueryable Data)
{
string query = Data.ToString();
int pIndex = query.LastIndexOf(')');
if (pIndex == -1)
pIndex = 0;
if (query.IndexOf("ORDER BY", pIndex) != -1)
{
return true;
}
return false;
}
I know it is extremely dirty but it works in all of my cases and I can't think of an exceptional case.
Solution 7:[7]
This Works for me.
When IQueryable list is being sorted (Ordered by) it's type is changed to IOrderedQueryable from IQueryable.
if (iQueryableList.Expression.Type == typeof(IOrderedQueryable<T>))
{
//IQueryable is sorted
}
else
{
//IQueryable is sorted
}
You can find more details about this on below post.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Bueller |
Solution 2 | CodingYourLife |
Solution 3 | |
Solution 4 | Matteo Mosca |
Solution 5 | |
Solution 6 | emregon |
Solution 7 | Ashen Upendra Disanayaka |