Что произойдет при использовании IEnumerable для работы с базой данных?
Что проверяют
Понимаете ли разницу между IQueryable<T> и IEnumerable<T> и видите ли момент, в котором запрос перестаёт превращаться в SQL и начинает считаться в памяти приложения.
Как отвечать
IQueryable<T> хранит дерево выражений, и провайдер доступа к данным переводит Where, OrderBy, Skip, Take в SQL, поэтому база возвращает только нужные строки. Как только последовательность приведена к IEnumerable<T>, будь то AsEnumerable(), ToList(), присваивание в переменную такого типа или возврат из метода с таким типом, выбираются методы обычного LINQ по объектам, которые принимают делегаты. Дальше всё считается на стороне приложения.
IQueryable<Order> q = db.Orders;
q.Where(o => o.Sum > 1000).Take(10); // условие и ограничение уходят в SQL
IEnumerable<Order> e = db.Orders;
e.Where(o => o.Sum > 1000).Take(10); // выбирается вся таблица, фильтр в памятиОтложенное выполнение остаётся в обоих случаях: запрос уходит в базу на первой итерации, а не в момент написания. Отсюда два следствия для практики. Повторный проход по одной и той же последовательности сходит в базу второй раз, поэтому результат, нужный дважды, материализуют. Итерация после того, как контекст закрыт, даёт исключение об освобождённом объекте.
Вывод для ответа: фильтровать и сортировать, пока последовательность ещё IQueryable, а переход в память делать явно и только там, где выражение в SQL не переводится.
Где спотыкаются
Называют отложенное выполнение ленивой загрузкой. Это разные вещи: ленивая загрузка это подтягивание связанных сущностей при обращении к навигационному свойству, и именно она порождает лишние запросы в цикле.
Второе: отдают IQueryable наружу из репозитория. Момент похода в базу и время жизни контекста уезжают в слой, который об этом не знает, и сборка запроса расползается по всему коду.
Третье: считают, что IEnumerable экономит память, раз данные не грузятся сразу целиком. Выходит наоборот: условие не попадает в SQL, база отдаёт всю таблицу, и приложение прочитает её целиком, чтобы отбросить почти всё.