Самые часто задаваемые вопросы на собеседовании по 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 = ...;Этот код выполнит SQL-запрос для выбора только золотых клиентов. А вот следующий код выполнит исходный запрос к базе данных, а затем отфильтрует не-золотых клиентов в памяти:
// Затем...
var goldCustomers = custs.Where(c => c.IsGold);
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