Когда ObjectQuery действительно является IOrderedQueryable?

Применительно к инфраструктуре сущности, методы расширения Select() и OrderBy() возвращают a ObjectQuery, который определяется как:

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

Тип возврата Select() равен IQueryable<T>, а <<26 > - IOrderedQueryable<T>. Таким образом, вы можете сказать, что оба возвращают один и тот же тип, но в другой оболочке. К счастью, потому что теперь мы можем применить ThenBy после вызова OrderBy.

Теперь моя проблема.

Скажем, у меня есть это:

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

Это дает мне IQueryable<Plot>, который является ObjectQuery<Plot>. Но это также IOrderedQueryable:

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

Но все же:

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

Когда я это сделаю:

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

Он компилируется, но дает исключение во время выполнения:

Выражение типа 'IQueryable`1[Plot]' не может использоваться для параметра типа 'IOrderedQueryable`1[Plot] 'метода' IOrderedQueryable`1[Plot] ThenBy[Plot,Nullable`1](IOrderedQueryable`1[Plot], Expressions.Expression`1[System.Func`2[Plot,System.Nullable`1[System.Int32]]]) '

Листинг выполняется (я проверил), но параметр ThenBy по-прежнему рассматривается как IQueryable (что меня немного озадачивает).

Теперь предположим, что какой-то метод возвращает ObjectQuery<Plot> мне как IQueryable<Plot> (например, Select()). Что делать, если я хочу знать, можно ли вызывать ThenBy на возвращаемом объекте. Как я могу понять это, если ObjectQuery является "реальным" или "поддельным" IOrderedQueryable, не перехватывая исключения?

Ответ 1

Выражение Деревья действительно весело! (или, может быть, я немного урод) и, вероятно, станет полезным во многих будущих разработках, если Project Roslyn - это что-то, что нужно сделать! =)

В вашем случае просто наследуйте от MSDN ExpressionVisitor и переопределите метод VisitMethodCall в наследующем классе с чем-то, чтобы сравнить m.MethodInfo с SortBy (т.е. если вы не слишком суетливы, просто проверьте имя, если вы хотите быть суетливым использованием отражения, чтобы схватить фактический SortBy MethodInfo для сравнения с.

Сообщите мне, если/что вам нужно, но, честно говоря, после копирования/вставки ExpressionVisitor вам, вероятно, понадобится не более 10 строк кода без выражения, -)

Надеюсь, что поможет

Ответ 2

Несмотря на то, что деревья выражений очень забавны, в этом случае простое решение не будет использовать OrderBy, а не ThenBy?

  • OrderBy является расширением на IQueryable и возвращает IOrderedQueryable.
  • ThenBy является расширением на IOrderedQueryable и возвращает IOrderedQueryable.

Итак, если у вас есть IQueryable (как в вашем случае выше, где запрос является IQueryable), и вы хотите применить к нему первоначальный заказ, используйте OrderBy. ThenBy предназначен только для применения дополнительного заказа к уже упорядоченному запросу.

Если у вас есть какой-то результат LINQ, но вы не уверены, что это IQueryable или IOrderedQueryable и хотите применить к нему дополнительную фильтрацию, вы можете сделать два метода:

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

и

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

Компилятор выберет правильный вызов для вызова на основе типа времени компиляции вашего объекта запроса.