31

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.

Bertrand Marron
  • 21,501
  • 8
  • 58
  • 94
  • If you're staying in the realm of `IQueryable`, do you need to worry? What I mean is, an extra `.OrderBy` might well result in no change to what's actually executed by the provider on the underlying data source. – AakashM Dec 14 '11 at 17:05
  • 1
    @AakashM, If I call `OrderBy` on an already sorted `IQueryable` I lose that sort. I just want to add to it. – Bertrand Marron Dec 14 '11 at 17:12
  • I was actually thinking you were worrying about perf. How about always using `ThenBy` ? – AakashM Dec 14 '11 at 17:15
  • 1
    @AakashM, Calling `ThenBy` on a non-ordered `IQueryable` doesn't make sense. – Bertrand Marron Dec 14 '11 at 17:37
  • It's in the default db order to start with, right? Why don't you try it? – AakashM Dec 14 '11 at 17:47
  • Possible duplicate of [How can I tell if an IQueryable is an IOrderedQueryable?](http://stackoverflow.com/questions/5071426/how-can-i-tell-if-an-iqueryable-is-an-iorderedqueryable) – Frédéric Nov 12 '15 at 12:06

7 Answers7

38

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>);
}
CodingYourLife
  • 7,172
  • 5
  • 55
  • 69
Zac Charles
  • 1,208
  • 14
  • 19
  • Thanks, that's what Ive looked for. I would just change the check for implementation of interface instead of equality of type ... typeof(IOrderedQueryable).IsAssingableFrom(queryable.Expression.Type) – Ondrej Jun 29 '16 at 13:53
  • 1
    What are the benefits of that change @Ondrej? I also noticed that neither work if OrderBy was not the most recent operation :( – Zac Charles Jun 30 '16 at 14:46
  • You just saved me from hours of pain! This is definitely the correct answer – BGilman May 16 '18 at 17:07
  • This should be the accepted answer.. I did not think of looking in the Expression instead of validating against the queryable itself. – Jobse Jan 25 '21 at 10:33
  • 2
    This will not work in some simple cases. For example in this case: ```csharp var query = queryable.OrderBy(e=>e.Id).AsNoTracking(); ``` `queryable.Expression.Type == typeof(IOrderedQueryable)` will be `False`. Simplest way to check is just check if whole expression is `IOrderedQueryable` in that way: ```csharp public static bool IsOrdered(this IQueryable queryable) { return queryable is IOrderedQueryable; } ``` – ZlobnyiSerg Oct 27 '21 at 15:15
7

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);
Duncan Smart
  • 31,172
  • 10
  • 68
  • 70
  • Great answer! This leverages the power of the .Net Expression classes, and will return the correct answer, even if the order by is not the most recent expression in the tree. – arhnee Feb 15 '23 at 14:43
1

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.

kemiller2002
  • 113,795
  • 27
  • 197
  • 251
1

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.

Matteo Mosca
  • 7,380
  • 4
  • 44
  • 80
0

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.

emregon
  • 388
  • 1
  • 7
  • 18
-1

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.

more details

-2

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

Bueller
  • 2,336
  • 17
  • 11
  • 9
    Then, how is [`.Skip`](http://msdn.microsoft.com/en-us/library/bb357513.aspx) able to [throw an exception](http://stackoverflow.com/questions/225481/how-to-check-for-the-presence-of-an-orderby-in-a-objectqueryt-expression-tree) when you use it on an unordered EF collection? – Bertrand Marron Jan 25 '12 at 16:14
  • 1
    I don't think .Skip does throw an exception. The underlying provider throws an exception when it tries to build a query from the *entire* IQueryable. – Craig Celeste Jun 14 '12 at 17:34
  • 1
    It's true that there's no flag as such, but it is possible to tell if an ordering method has been called. See [my answer](http://stackoverflow.com/a/31252271/964514). – Zac Charles Jul 06 '15 at 17:44