4

Applied to entity framework, the extension methods Select() and OrderBy() both return an ObjectQuery, which is defined as:

public class ObjectQuery<T> : ObjectQuery, IOrderedQueryable<T>,
    IQueryable<T>, <... more interfaces>

The return type of Select() is IQueryable<T> and that of OrderBy is IOrderedQueryable<T>. So you could say that both return the same type but in a different wrapper. Luckily so, because now we can apply ThenBy after OrderBy was called.

Now my problem.

Let's say I have this:

var query = context.Plots.Where(p => p.TrialId == 21);

This gives me an IQueryable<Plot>, which is an ObjectQuery<Plot>. But it is also an IOrderedQueryable:

var b = query is IOrderedQueryable<Plot>; // True!

But still:

var query2 = query.ThenBy(p => p.Number); // Does not compile.
// 'IQueryable<Plot>' does not contain a definition for 'ThenBy'
// and no extension method 'ThenBy' ....

When I do:

var query2 = ((IOrderedQueryable<Plot>)query).ThenBy(p => p.Number);

It compiles, but gives a runtime exception:

Expression of type 'IQueryable`1[Plot]' cannot be used for parameter of type 'IOrderedQueryable`1[Plot]' of method 'IOrderedQueryable`1[Plot] ThenBy[Plot,Nullable`1](IOrderedQueryable`1[Plot], Expressions.Expression`1[System.Func`2[Plot,System.Nullable`1[System.Int32]]])'

The cast is carried out (I checked), but the parameter of ThenBy is still seen as IQueryable (which puzzles me a bit).

Now suppose some method returns an ObjectQuery<Plot> to me as IQueryable<Plot> (like Select()). What if I want to know whether it is safe to call ThenBy on the returned object. How can I figure it out if the ObjectQuery is "real" or a "fake" IOrderedQueryable without catching exeptions?

AakashM
  • 62,551
  • 17
  • 151
  • 186
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • 2
    I'd advise against checking the concrete type as it will tie you to your query provider which is exactly what `IQueryable` avoids. One approach is a quick walk of the expression to check for the `SortBy()` method but I suspect somebody will point out something easier. If not, comment on here and I'll provide you the code to walk the tree. Good luck – Smudge202 Jan 26 '12 at 20:57
  • @Smudge202 Thanks. There are a lot places much more fun to walk than expression trees, but if you can't resist... But yes, maybe someone else. – Gert Arnold Jan 26 '12 at 20:59
  • @Smudge202 btw, checking the concrete type wouldn't work anyway because it implements both interfaces. It puzzles me that ordinary reflection does not help here, so probably a deeper inspection as you suggest is the only way if I really need to know. – Gert Arnold Jan 27 '12 at 08:31
  • Yeh, I didn't think it possible by examining concrete types - perhaps a saving grace? =) Good luck with the deeper inspection. I suspect it may be possible through some form of reflection, but definitely possible as advised through expression trees. Good luck on either approach. – Smudge202 Jan 27 '12 at 09:21

2 Answers2

3

Expression Trees are genuinely good fun! (or perhaps I'm a little bit of a freak) and will likely become useful in many a developer's future if Project Roslyn is anything to go by! =)

In your case, simple inherit from MSDN's ExpressionVisitor, and override the VisitMethodCall method in an inheriting class with something to compare m.MethodInfo with SortBy (i.e. if you're not too fussy simply check the name, if you want to be fussy use reflection to grab the actual SortBy MethodInfo to compare with.

Let me know if/what you need examples of, but honestly, after copy/pasting the ExpressionVisitor you'll probably need no more than 10 lines of non-expression-tree code ;-)

Hope that helps

Smudge202
  • 4,689
  • 2
  • 26
  • 44
  • There, you did it. You infected me with the expression tree virus! It is a viable solution (btw the method name is "OrderBy") albeit specific to expressions and you're right, it's fun! So far for my walks in the park :). Although this worked here, I will also post a question that articulates the problem in pure C# terms. – Gert Arnold Jan 30 '12 at 10:52
  • For who is interested: [here](http://stackoverflow.com/questions/9063131/how-to-tell-which-interface-is-returned-by-a-method) is the pure C# question. – Gert Arnold Jan 30 '12 at 13:08
1

Although Expression Trees are good fun, wouldn't in this case the simple solution be to use OrderBy rather than ThenBy?

  • OrderBy is an extension on IQueryable and returns an IOrderedQueryable.
  • ThenBy is an extension on IOrderedQueryable and returns an IOrderedQueryable.

So if you have a IQueryable (as in your case above, where query is an IQueryable) and you want to apply an initial ordering to it, use OrderBy. ThenBy is only intended to apply additional ordering to an already ordered query.

If you have a LINQ result of some kind, but you aren't sure if it is an IQueryable or an IOrderedQueryable and want to apply additional filtering to it, you could make two methods like:

 static IOrderedQueryable<T, TKey> ApplyAdditionalOrdering<T, TKey>(this IOrderedQueryable<T, TKey> source, Expression<Func<T, TFilter>> orderBy)
        {
            return source.ThenBy(orderBy);
        }

And

static IOrderedQueryable<T, TKey> ApplyAdditionalOrdering<T, TKey>(this IQueryable<T> source, Expression<Func<T, TFilter>> orderBy)
        {
            return source.OrderBy(orderBy);
        }

The compiler will figure out the correct one to call based on the compile-time type of your query object.

Mike Sackton
  • 1,094
  • 7
  • 19
  • That's a smart solution. One drawback, it can't be used *inside* and expression. In the mean time I've never really had any need to know if an `IQueryable` had an initial sorting. It was only that I was puzzled by this no-op interface. – Gert Arnold Aug 12 '16 at 23:47