Ответ
Оптимизация запросов на чтение в EF Core — ключевой навык для повышения производительности приложения. Вот основные практики:
1. Отключение отслеживания изменений (AsNoTracking)
Используйте AsNoTracking() для запросов, где данные только читаются и не будут изменяться. Это исключает накладные расходы на создание и хранение объектов ChangeTracker.
var products = await context.Products
.AsNoTracking()
.Where(p => p.Price > 100)
.ToListAsync();
2. Точечная выборка данных (Проекции)
Никогда не выбирайте всю сущность (SELECT *), если нужны лишь несколько полей. Используйте Select.
var productData = await context.Products
.Where(p => p.IsActive)
.Select(p => new { p.Id, p.Name, p.Price }) // Анонимный тип или DTO
.ToListAsync();
3. Избирательная загрузка связанных данных
Избегайте Include без необходимости. Используйте Select для явной загрузки нужных полей связанных сущностей, чтобы предотвратить проблему SELECT N+1 и избыточные JOIN.
// Плохо: может загрузить все поля Order и Customer
var orders = context.Orders.Include(o => o.Customer).ToList();
// Хорошо: загружаем только нужные данные
var orders = context.Orders
.Select(o => new {
OrderId = o.Id,
CustomerName = o.Customer.Name,
Total = o.TotalAmount
}).ToList();
4. Пакетирование запросов
EF Core 5+ группирует несколько операций SaveChanges в один вызов к БД. Для чтения используйте явное объединение логики в один запрос, а не несколько последовательных ToListAsync.
5. Использование индексов на уровне БД
Убедитесь, что поля, используемые в условиях Where, OrderBy и Join, проиндексированы. Это решает проблему на уровне источника данных.
6. Кэширование
Для статических или редко меняющихся данных (справочники) реализуйте кэширование (например, с помощью IMemoryCache или IDistributedCache), чтобы полностью избежать обращения к базе.
var categories = await memoryCache.GetOrCreateAsync("all_categories", async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
return await context.Categories.AsNoTracking().ToListAsync();
});
7. Анализ запросов
Всегда проверяйте сгенерированный SQL через context.LogTo(Console.WriteLine) или профилировщик БД, чтобы убедиться в его эффективности.