Ответ
Оптимизация запросов EF Core — это многоуровневый процесс. Вот ключевые стратегии:
1. Выбирайте только нужные данные (Projection)
Избегайте SELECT *. Используйте Select() для загрузки конкретных полей или анонимных типов.
// ПЛОХО: Загружает все поля User
var users = await context.Users.ToListAsync();
// ХОРОШО: Загружает только Id и Name
var users = await context.Users
.Select(u => new { u.Id, u.Name })
.ToListAsync();
2. Отключайте отслеживание изменений для запросов только для чтения
Метод AsNoTracking() значительно ускоряет выполнение, так как контекст не создает "снимки" сущностей.
var products = await context.Products
.AsNoTracking() // Критически важно для read-only сценариев
.Where(p => p.IsActive)
.ToListAsync();
3. Эффективно загружайте связанные данные
- Жадная загрузка (
Include/ThenInclude): Используйте для данных, которые точно понадобятся. Будьте осторожны с цепочками, чтобы не получить "звездный" запрос. - Явная загрузка (
Load): Для загрузки по требованию. - Выборочная загрузка (Projection с Include): Самый эффективный способ.
// Эффективно: Загружает только имена заказов и клиентов var orders = await context.Orders .Where(o => o.Date.Year == 2023) .Select(o => new { o.Id, o.Total, CustomerName = o.Customer.Name // Загружается в том же запросе! }) .ToListAsync();
4. Фильтруйте и пагинируйте данные на стороне БД
Всегда используйте Where(), Take(), Skip() до вызова ToListAsync().
// Пагинация на стороне БД
var page = await context.Products
.Where(p => p.CategoryId == 5)
.OrderBy(p => p.Name)
.Skip(20)
.Take(10)
.AsNoTracking()
.ToListAsync();
5. Используйте индексы в базе данных EF Core может создавать их через миграции, но проектирование индексов — задача разработчика/администратора БД.
6. Анализируйте сгенерированный SQL
var query = context.Users.Where(u => u.IsActive);
var sql = query.ToQueryString(); // Получить SQL
// Или используйте логирование (context.LogTo) или профилировщик БД.
7. Избегайте проблемы N+1 Плохой паттерн (N+1 запрос):
var authors = context.Authors.ToList(); // 1 запрос
foreach (var a in authors)
{
// N запросов (по одному на каждого автора)!
var books = context.Books.Where(b => b.AuthorId == a.Id).ToList();
}
Исправление: Используйте Include или проекцию с Select для загрузки всех данных за один запрос.