Проблема N+1 запроса: виноват не ORM, а ваш код

srogatnev 3 минуты назад Проблема N+1 запроса: виноват не ORM, а ваш код Средний 7 мин 32 Блог компании Контур C# * .NET * Программирование * У меня есть несколько статей, в которых я рассказываю о различных...
<5 — 2026'da uzaya kaç SpaceX Starship fırlatması ulaşacak?
Значимый прорыв формирует отрасль ИИ: srogatnev 3 минуты назад Проблема N+1 запроса: виноват не ORM, а ваш код Средний 7 мин 32 Блог компании Контур C# * . NET * Программирование * У меня есть несколько статей, в которых я рассказываю о различных особенностях работы коллекций в . NET, например эта и эта.
Мы с вами разбирали, как лучше искать в коллекциях, спорили о преимуществах Any и Count в борьбе за тысячные доли секунды. Но все усилия, затраченные на эти микрооптимизации, могут пойти крахом из-за одного неудачного запроса в базу или другой сервис. На этот раз я предлагаю рассмотреть проблему N+1 запроса.
Технические детали
Что это такоеПроблема пришла к нам из мира ORM, где lazy-loading легко может создать нам проблем:// 1 запрос — получим список из N блогов var blogs = await _context. ToListAsync(); foreach (var blog in blogs) { // Ещё N запросов, где мы считаем количество постов в каждом блоге Console. Count); } // Всего 1+N запросов!
Но и без lazy-loading можно натворить делов:public async Task> GetUsers(Guid ids) { var list = new List(); foreach (var id in ids) { var user = await _context. FirstOrDefaultAsync(o => o. Id == id); if (user !
Add(user); } } return list; }И проблемы, порожденные ORM мы решаем средствами ORM. Например, в Entity Framework (EF) используем метод Include:// 1 запрос — получим список из N блогов и всех постов var blogs = await _context. ToListAsync(); foreach (var blog in blogs) { // Не будет лишних запросов Console.
Отраслевые последствия
Count); } // 1 запрос, чтобы выбрать все данныеНо такое решение имеет ограниченную применимость. Используя Include, мы говорим EF вобрать данные одним запросом, для чего он использует JOIN. А если представить, что у нас есть 20 блогов, а в каждом по 100 постов, то в результате получим выборку из 20 x 100 = 2000 строк, потому что для будет дублирование родительской сущности для каждой дочерней.
Хотя уникальных данных у нас всего на 120 строк. А представьте, что может быть, если мы добавим еще Include и выберем еще вложенные сущности? var blogs = await _context.
ToListAsync();Эта ситуация называется декартов взрыв (или картезианский взрыв) — когда количество строк в итоговой выборке растет лавинообразно с добавлением нового измерения через Include. Для решения этой проблемы добавили новую возможность разделения на независимые запросы через метод AsSplitQuery:// 2 запроса — получить список блогов и список всех постов var blogs = await _context. ToListAsync(); foreach (var blog in blogs) { // Не будет лишних запросов Console.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





