N+1 Problemi
EF Core'a yeni başlayan herkesin bir noktada çarptığı duvar: N+1 problemi. Bir liste döndürüp her elemanın ilişkisini yan dönüş ettirdiğinizde, 1 sorgu yerine 1+N sorgu çalışır.
// YANLIŞ — her müşteri için ayrı sorgu
var musteriler = await db.Musteriler.ToListAsync();
foreach (var m in musteriler)
{
Console.WriteLine(m.Siparisler.Count); // her birinde DB'ye gider
}
Çözüm Include kullanmak:
var musteriler = await db.Musteriler
.Include(m => m.Siparisler)
.ToListAsync();
Projection ile Sadece Gerekli Alanlar
Listede sadece 3 alan gösteriyorsanız, tüm sütunları çekmenize gerek yok. Select ile DTO'ya projeksiyon yapın:
var liste = await db.Projeler
.Select(p => new ProjeKartDto
{
Id = p.Id,
Baslik = p.Baslik,
Yil = p.Yil
})
.ToListAsync();
Bir kurumsal CRM projesinde bu tek değişiklik liste sayfasını 1.4s'den 200ms'ye indirdi.
AsNoTracking
EF Core varsayılan olarak çektiği her satırı "izler"; değişiklikleri tespit edebilmek için. Ama liste sayfalarında bu izlemeye ihtiyaç yok:
var liste = await db.Urunler
.AsNoTracking()
.Where(u => u.Aktif)
.ToListAsync();
Sadece okuma yapan tüm sorgulara bunu ekleyin. Memory kullanımı düşer, sorgu hızlanır.
Toplu İşlemler
1000 kayıt güncellemek için 1000 ayrı SQL UPDATE çalıştırmak çok pahalıdır. EF Core 7+ ile gelen ExecuteUpdate ve ExecuteDelete bunu tek sorguda halleder:
await db.Siparisler
.Where(s => s.Durum == "Bekliyor" && s.TarihUtc < tarih)
.ExecuteDeleteAsync();
builder.LogTo(Console.WriteLine) ile loglayın. Yavaş çalışan her sorgunun nedenini SQL'e bakarak anlayabilirsiniz.
Sonuç
EF Core hızlı ama "ne istediğinizi biliyor" gibi davranmıyor. Onun adına ne istediğinizi siz net söylüyorsunuz: AsNoTracking, Select, Include, ExecuteUpdate. Bu 4 araç, bir kurumsal panelin performansı için %80 yetiyor.