Ответ 1
Одна из самых больших путаниц в запросах LINQ против IQueryable<T>
заключается в том, что они выглядят точно так же, как запросы от IEnumerable<T>
. Ну, первый использует Expression<Func<..>>
всякий раз, когда позже использует Func<..>
, но кроме случаев использования явных деклараций это не так заметно и кажется несущественным. Однако большая разница возникает во время выполнения. Как только запрос IEnumerable<T>
успешно скомпилирован, во время выполнения он просто работает, что не соответствует IQueryable<T>
. Запрос IQuaryable<T>
- это фактически дерево выражений, которое обрабатывается во время выполнения поставщиком запроса. С одной стороны это большое преимущество, с другой стороны, поскольку поставщик запросов не участвует во время компиляции запросов (все методы предоставляются в виде методов расширения классом Queryable
), нет способа узнать, является ли поставщик поддерживает некоторую конструкцию/метод или нет до выполнения. Люди, которые используют Linq для Entities, знают это очень хорошо. Чтобы сделать все сложнее, нет четкой документации, которую поддерживает конкретный поставщик запросов и что еще более важно, что он не поддерживает (как вы заметили из ссылки "что поддерживается" ).
Какое решение (и почему работает ваш второй код)
Хитрость заключается в том, чтобы написать максимально возможную (запрошенную поставщиком запроса) часть запроса против IQueryable<T>
, а затем переключиться на IEnumerable<T>
и сделать остальное (помните, как только скомпилированный, IEnumerable<T>
запрос работает просто). Переключатель выполняется вызовом AsEnumerable()
. И почему ваш второй код работает, потому что неподдерживаемый Any
метод больше не встречается в контексте поставщика запросов DocumentDb. Обратите внимание, что вызов ToList
не нужен, и запрос не выполняется дважды - на самом деле так нет единого запроса, а два - один в базе данных и один в памяти. Так что чего-то подобного было бы достаточно
List<Art> items = DocumentDbHelper.Client.CreateDocumentQuery<Art>(collection.DocumentsLink)
.Where(i => i.type == "art")
.AsEnumerable() // The context switch!
.Where(i => i.Products.Any(p => p.Name == productType))
.ToList();
Наконец, что действительно поддерживается поставщиком запросов DocumentDb
Это не совсем понятно из документации, но ответ: точно (и только), что там содержится. Другими словами, только поддерживаемые операторы запросов (или, лучше сказать, методы расширения Queryable
или Enumerable
) являются
- Выберите
- SelectMany
- Где
- OrderBy
- OrderByDescending
Как вы можете видеть, он очень ограничен. Забудьте о операторах объединения и группировки, Any
, Contains
, Count
, First
, Last
и т.д. Единственное, что легко запомнить:
Откуда я знаю это? Ну, как обычно, когда что-то неясно из документации, либо используется пробная версия, либо ошибка или декомпилятор. По-видимому, в этом случае первое не применимо, поэтому я использовал это позже. Если вам интересно, используйте свой любимый декомпилятор и проверьте код внутреннего класса DocumentQueryEvaluator
внутри Microsoft.Azure.Documents.Client.dll
.