When is ObjectQuery really an IOrderedQueryable?

那年仲夏 提交于 2019-12-18 08:56:41

问题


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?


回答1:


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




回答2:


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.



来源:https://stackoverflow.com/questions/9025137/when-is-objectquery-really-an-iorderedqueryable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!