Как найти и исправить N+1 в EF Core
Вопрос
Как вы находите и исправляете проблему N+1 в приложении на EF Core?
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Признаки: число запросов растёт с размером данных, много одинаковых SELECT
Диагностика: логирование SQL, MiniProfiler, APM, счётчик команд в тестах
Include и ThenInclude для нужных связей
Проекции через Select в DTO, подсчёты на стороне базы
AsSplitQuery против декартова взрыва
Отключённая ленивая загрузка как защита по умолчанию
Что могут спросить следом
- Когда Include хуже проекции?
- Как написать тест, который упадёт при появлении N+1?
Разбор
N+1 появляется, когда загружаем список, а затем в цикле обращаемся к связям каждой записи: ленивая загрузка или явные запросы в цикле дают по запросу на элемент. Признак: число SQL-запросов растёт вместе с размером данных.
Нахожу через логирование SQL EF Core в Development, MiniProfiler или трейсинг в APM, где видно десятки одинаковых SELECT. Можно поставить в интеграционный тест перехватчик команд и проверять, что эндпоинт делает фиксированное число запросов.
Исправляю по ситуации. Если нужны сущности целиком, Include и ThenInclude, а для нескольких коллекций AsSplitQuery, чтобы не получить декартов взрыв. Для чтения чаще всего лучше проекция: Select в DTO только с нужными полями, а Count и Sum считаются в SQL. Ленивую загрузку держу выключенной: она удобна, но прячет запросы.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.