15

I have an IQueryable. I have not called OrderBy on it or otherwise done anything with it.

If I do:

// for some reason, isItOrdered is always true
var isItOrdered = myQueryable is IOrderedQueryable<T>

Why is this always true? (It seems like it shouldn't be.) And, more importantly, how can I tell if an IQueryable already has been ordered? (i.e. is truly an IOrderedQueryable)

I would like to be able to do something like:

if (myQueryable is IOrderedQueryable<T>)
  myQueryable = myQueryable.ThenBy(...);
else
  myQueryable = myQueryable.OrderBy(...);
Pedro
  • 382
  • 2
  • 7
  • 1
    you should see this answer http://stackoverflow.com/questions/36923850/how-to-know-if-orderby-was-applied-to-query – yosbel Apr 28 '16 at 20:24

3 Answers3

15

You haven't shown what's creating your queryable to start with, but perhaps it's naturally ordered in some way?

What you've got does check whether it's really an IOrderedQueryable<T> - I suspect that it's just that your query provider always provides an ordered queryable, even if the order isn't obvious.

EDIT: Okay, something else you might try:

if (typeof(IOrderedQueryable<T>).IsAssignableFrom(myQueryable.Expression.Type))

... or in general, print out myQueryable.Expression.Type and see what it looks like.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • The problem is that I get a nasty exception when I try to to the ThenBy. It goes something like this: Expression of type System.Linq.IQueryable cannot be used for parameter of type IOrderedQueryable of method IOrderedQueryable ThenBy .... – Pedro Feb 21 '11 at 21:30
  • 2
    The problem with Entity Framework is that EF `DbSet` inherits from `DbQuery` which itself implements `IOrderedQueryable`! So your statement always returns `true` if `myQueryable` is an EF query but not sorted at all. – Masood Khaari Sep 10 '14 at 07:41
  • 1
    @MassoodKhaari: Ick - that's horrible :( My answer still answers the question as asked of course - it just means that it's not a *useful* question to be asking... – Jon Skeet Sep 10 '14 at 12:19
  • But I think this is the exact source of the questioned problem. He has an `IQueryable` object with no call to `OrderBy` at all. But the statement `myQueryable is IOrderedQueryable` always returns `true`. The implication is that one can create an `IOrderedQueryable` object which is indeed unsorted. And the other implication is that EF exactly do such a weird thing. (It internally checks the query object expression instead to detect if it is ordered through calls to `OrderBy` or `OrderByDescending`.) – Masood Khaari Sep 10 '14 at 12:38
  • @JonSkeet Your solution using IsAssignableFrom(myQueryable.Expression.Type)) worked for me, btw. Thanks! – Bradley Mountford Mar 18 '15 at 17:13
  • Unfortunately `IsAssignableFrom` is not available in dnxcore50. The fact that we can't call ThenBy on an IOrderedQueryable seems like a bug to me. This seems to be the only work around. `if (query.Expression.Type == typeof(IOrderedQueryable))` – Dave Sep 14 '15 at 14:58
  • @Dave: Are you sure this isn't just a matter of using `TypeInfo`? That tends to be the way of things with reflection in dnxcore... – Jon Skeet Sep 14 '15 at 14:59
  • 2
    It helps me well, but was still not enough for all my cases. When the EF6 `DbSet` was only touched by `Include` (no `Where`), testing `IsAssignableFrom` is still returning `true` on unsorted queries. To fix that, I had to change the test to: `typeof(IOrderedQueryable).IsAssignableFrom(query.Expression.Type) && query.Expression.Type != typeof(System.Data.Entity.Core.Objects.ObjectQuery)`. Even uglier. – Frédéric Nov 12 '15 at 20:14
8

This seems to work

if (query.Expression.Type == typeof(IOrderedQueryable<T>))
    myQueryable = myQueryable.ThenBy(...);
else
    myQueryable = myQueryable.OrderBy(...);
John
  • 1,286
  • 2
  • 14
  • 22
1

Some IQueryable implementations reuse the same class for IOrderedQueryable<T>.

There isn't much of a point in checking if it's really already ordered unless you know how it's ordered, otherwise you might order by the exact same property when you call ThenBy().

Also, you can't call Queryable.ThenBy() on myQueryable if it's a reference to IQueryable—you have to cast it first:

if (myQueryable is IOrderedQueryable<T>)
   myQueryable = ((IOrderedQueryable<T>) myQueryable).ThenBy(...);
Mark Cidade
  • 98,437
  • 31
  • 224
  • 236
  • True. But in my case it isn't a problem if I have a redundant ordering. I get a crazy exception as I mentioned in the comment to Jon Skeet. – Pedro Feb 21 '11 at 21:33
  • Yes---I do cast it first. I still get the exception. – Pedro Feb 21 '11 at 22:14
  • @MarkCidade I'm having this same issue, using the code you posted above. @Pedro Are you using Entity Framework 7 by any chance? `var myQueryable = context.Table.Select(x => x);` or `var myQueryable = from x in context.Table select x` both return IOrderedQueryable, but calling ThenBy() throws an ArgumentException at runtime. – Dave Sep 15 '15 at 19:40
  • The exception says: Additional information: Expression of type 'System.Linq.IQueryable`1[Models.Table]' cannot be used for parameter of type 'System.Linq.IOrderedQueryable`1[Models.Table]' of method 'System.Linq.IOrderedQueryable`1[Models.Table] ThenBy[Table,String](System.Linq.IOrderedQueryable`1[Models.Table], System.Linq.Expressions.Expression`1[System.Func`2[Models.Table,System.String]])' – Dave Sep 15 '15 at 19:41