Когда 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);
}
Компилятор выберет правильный вызов для вызова на основе типа времени компиляции вашего объекта запроса.