Рассмотрим следующий фрагмент:
IQueryable<Person> pQ = from p in db.People select p;
IEnumerable<Person> pE = pQ;
Console.WriteLine(pQ.Count());
Console.WriteLine(pE.Count());
Оба дают одинаковый результат, но если вы отслеживаете сгенерированную SQL, то версия IQueryable
выдает запрос, используя COUNT
, тогда как IEnumerable
версия просто выдает SELECT
, вытягивая все строки и делая подсчет в памяти, что потенциально очень неэффективно. То же самое относится и к другим методам, таким как Sum()
и Average()
, и происходит как в EF6, так и в EF Core 3.
Причина в том, что Count()
является методом расширения и поэтому привязывается к состоянию c тип переменной (IQueryable
в первом случае, IEnumerable
во втором), а не тип Dynami c (одинаковый в обоих случаях, поскольку это один и тот же объект).
Это это действительно неприятная ошибка, то есть обычно лучше избегать IEnumerable
в таких случаях. Однако есть обходной путь:
Console.WriteLine(pE.AsQueryable().Count());
, что означает, что SQL
COUNT
выпущен.
(Примечание: приведение к IQueryable
также будет работать здесь, но не в Вообще - если бы IEnumerable
просто ссылался на данные в памяти, тогда приведение не состоялось бы, но AsQueryable()
просто окружило бы объект в нейтральной оболочке.)
Мой вопрос такой: так как эта проблема это так просто упустить и может вызвать такое неэффективное поведение, почему вызов AsQueryable()
не просто встроен в IEnumerable
реализации методов расширения?