День сто двадцать восьмой. #ВопросыНаСобеседовании

Самые часто задаваемые вопросы на собеседовании по C#

14. В чём разница между IEnumerable и IQueryable?

При работе с базой данных иногда можно запутаться. Могут возникнуть некоторые вопросы, например, что использовать для извлечения данных из БД, IEnumerable или IQueryable?

Если коротко:

- IEnumerable - выполняет запрос SELECT на стороне сервера, загружает все данные в память на стороне клиента, а затем выполняет фильтрацию.

- IQueryable - выполняет запрос SELECT на стороне сервера со всеми фильтрами.

Разница в том, что IQueryable<T> - это интерфейс, который позволяет работать LINQ-to-SQL (на самом деле, LINQ-to-чтоугодно). Поэтому, если вы дополнительно уточните свой запрос в IQueryable<T>, этот запрос будет выполнен в базе данных, если это возможно.

В случае IEnumerable<T> это будет LINQ-to-object, то есть все объекты, соответствующие исходному запросу, должны быть загружены в память из базы данных:

IQueryable<Customer> custs = ...;

// Затем...

var goldCustomers = custs.Where(c => c.IsGold);

Этот код выполнит SQL-запрос для выбора только золотых клиентов. А вот следующий код выполнит исходный запрос к базе данных, а затем отфильтрует не-золотых клиентов в памяти:

IEnumerable<Customer> custs = ...;

// Затем...

var goldCustomers = custs.Where(c => c.IsGold);

Это довольно важное различие, и работа с IQueryable может во многих случаях избавить вас от возвращения слишком большого количества строк из базы данных. Другим ярким примером является разбиение на страницы: если вы используете Take и Skip с IQueryable, вы получите только запрошенные строки, а выполнение этого в IEnumerable приведет к загрузке в память всех строк.

По сути, есть два идентичных набора расширений LINQ. Where, Sum, Count, FirstOrDefault и т.д. имеют две версии: одна, которая принимает функции, и другая, которая принимает выражения.

Сигнатура для IEnumerable: Where(Func<Customer, bool> predicate)

Сигнатура для IQueryable: Where(Expression<Func<Customer, bool>> predicate)

Вы, вероятно, использовали обе версии, не осознавая этого, потому что обе вызываются с использованием идентичного синтаксиса Where(x => x.City == "<City>"), который работает как для IEnumerable, так и для IQueryable.

При использовании Where с IEnumerable компилятор передает в Where скомпилированную функцию. А при использовании Where с IQueryable компилятор передает в Where дерево выражений. Дерево выражений похоже на отражение, но для кода. Компилятор преобразует ваш код в структуру данных, которая описывает, что ваш код делает в формате, который легко усваивается.



Зачем заморачиваться с этими деревьями выражений? Я просто хочу, чтобы Where фильтровал мои данные. Основная причина в том, что Entity Framework и Linq2SQL могут преобразовывать деревья выражений непосредственно в SQL, поэтому ваш код будет выполняться намного быстрее.

Это похоже на бесплатное повышение производительности. То есть надо везде использовать AsQueryable? Нет, IQueryable полезен, только если базовый поставщик данных может что-то с ним сделать. Преобразование обычного List в IQueryable не даст вам никакой выгоды.



Источники:

-
https://www.c-sharpcorner.com

-
https://stackoverflow.com/questions/2876616/returning-ienumerablet-vs-iqueryablet