TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.78K subscribers
Post #1046 1.33K
День восемьсот пятьдесят седьмой. #ЗаметкиНаПолях
Настройка Производительности в EF Core
Сегодня посоветую вам видео из серии EF Core Community Standup, посвящённое настройке производительности в Entity Framework. Для знающих SQL, вроде меня, советы могут показаться довольно очевидными, хотя интересно было узнать, что оптимизации можно делать не в SQL, а прямо в LINQ запросе к EF.

Для примера взято приложение книжного магазина. Есть основная сущность Books. Сущности Authors (авторы) и Tags (теги), которые относятся к Books как многие-ко-многим, а сущность Reviews (отзывы и оценки) – один-ко-многим.
Нам для отображения доступных книг нужно выбрать данные книги (название, цена, год публикации), авторов и теги для каждой книги, а также количество отзывов и среднюю оценку.

Очевидным вариантом запроса будет что-то вроде:
var books = context.Books
.Include(book => book.AuthorsLink
.OrderBy(bookAuthor => bookAuthor.Order))
.ThenInclude(bookAuthor => bookAuthor.Author)
.Include(book => book.Reviews)
.Include(book => book.Tags)
.ToList();

Как можно улучшить этот запрос:
1. Добавить метод AsNoTracking() для всех запросов, не предполагающих изменения данных.

2. Не загружать ненужные данные. Используйте проекцию в методе Select для выбора только нужных полей:
Books.Select(p => new BookListDto {
Title = p.Title,
Price = p.Price,
Published = p.Published,
…

3. Не включать зависимости, а выбирать только то, что надо из них. Например, нам нужны только имена авторов через запятую:
  AuthorsOrdered = string.Join(", ",
p.AuthorsLink.OrderBy(q => q.Order)
.Select(q => q.Author.Name)),
…
String.Join будет выполняться на клиенте, но можно использовать пользовательские функции SQL (User-Defined Functions).

4. По возможности выполнять вычисления на стороне БД. Например, для количества отзывов и средней оценки (заметьте, что требуется приведение к обнуляемому типу и тип зависит от конкретной СУБД):
  ReviewsCount = p.Reviews.Count(),
ReviewsAverageVotes =
p.Reviews.Select(y =>
(double?)y.NumStars).Average(),
…

5. В конфигурации сущностей EF добавить индексы для каждого свойства сущности, по которому планируется производить сортировку или фильтрацию.

Чтобы не загромождать запрос, все эти изменения можно разместить в методе расширения MapBookToDto(this IQueryable<Book> books), таким образом сам запрос будет выглядеть примерно так:
var booksQuery = _context.Books 
.AsNoTracking()
.MapBookToDto()
. …

Если эти советы всё равно не дают достаточной производительности, можно, например, денормализовать БД и включить столбцы с рассчитанными значениями количества отзывов и средней оценкой в таблицу Books. А при добавлении/удалении отзыва пересчитывать эти значения.

В видео по ссылке ниже весь процесс оптимизации производительности описан более подробно и наглядно.

Источники:
- Видео
https://youtu.be/VgNFFEqwZPU
- Текст
https://www.thereformedprogrammer.net/five-levels-of-performance-tuning-for-an-ef-core-query/
More from @netdeveloperdiary
  1. Oct 17, 2026День 2817. #ЧтоНовенького #VS Прекращаем Сборку После Первой Ошибки Мэдс Кристенсен продол…
  2. Oct 16, 2026День 2816. #Карьера #AI Станет ли ИИ-Кодинг Концом Программирования для Людей? Автор ориги…
  3. Oct 15, 2026Post #3374
  4. Oct 14, 2026Post #3373
  5. Oct 13, 2026День 2813. #ЗаметкиНаПолях #Async Ещё Раз Про Async/Await в C#. Часть 1 Почему существует…
  6. Oct 12, 2026День 2812. #Карьера #AI Как Разработчикам Оставаться Полезными, Когда Код Пишет ИИ? По мер…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →