Entity Framework Performansını Artıran 10 Altın Kural

Entity Framework Performansını Artıran 10 Altın Kural

Bu makale Entity Framework Core kullanırken en sık yapılan performans hatalarını ve doğru kullanım örneklerini göstermektedir.

1. ToList() Metodunu Çok Erken Çağırmayın

Entity Framework Core'da en sık yapılan performans hatalarından biri, ToList() metodunu sorgunun başında çağırmaktır. ToList() çalıştığı anda sorgu veritabanına gönderilir ve tüm kayıtlar uygulamanın belleğine yüklenir. Daha sonra yazılan Where(), OrderBy() gibi işlemler artık SQL Server üzerinde değil, C# tarafında çalışır.

Bu durum özellikle yüz binlerce kayıt bulunan tablolarda gereksiz RAM kullanımı ve yavaş sorgulara neden olur. Önce filtreleme yapılmalı, en son ToListAsync() çağrılmalıdır.

Yanlış Kullanım


var liste = context.Personeller
    .ToList()
    .Where(x => x.Aktif);

Doğru Kullanım


var liste = await context.Personeller
    .Where(x => x.Aktif)
    .ToListAsync();

Sonuç: Daha az veri okunur, daha az RAM kullanılır ve sorgu doğrudan SQL Server üzerinde çalışır.

2. AsNoTracking() Kullanın

Entity Framework varsayılan olarak okunan tüm nesneleri takip eder (Change Tracking). Güncelleme yapılmayacak sorgularda bu takip işlemi gereksizdir ve performansı olumsuz etkiler.

Listeleme, raporlama ve arama ekranlarında AsNoTracking() kullanılması bellek kullanımını azaltır ve sorguların daha hızlı çalışmasını sağlar.


var liste = await context.Personeller
    .AsNoTracking()
    .ToListAsync();

Sonuç: Daha düşük bellek kullanımı ve daha hızlı okuma işlemleri.

3. Sadece İhtiyacınız Olan Alanları Çekin

Bir tabloda onlarca sütun bulunabilir. Eğer ekranda sadece Ad Soyad ve Telefon gösterilecekse tüm kolonları çekmenin bir anlamı yoktur.

Select() kullanarak sadece gerekli alanları seçmek ağ trafiğini azaltır ve sorguların daha hızlı çalışmasını sağlar.


var personeller = await context.Personeller
.Select(x => new
{
    x.Id,
    x.AdSoyad,
    x.Telefon
})
.ToListAsync();

Sonuç: Daha küçük veri transferi ve daha yüksek performans.

4. N+1 Query Probleminden Kaçının

Entity Framework'te performansı en çok düşüren problemlerden biri N+1 Query Problemidir. Bu problem, ana veriler getirildikten sonra her kayıt için ayrı bir SQL sorgusu çalıştırılması sonucu oluşur.

Örneğin 100 sipariş kaydı çekildiğinde, her siparişin müşteri bilgisi ayrı ayrı okunuyorsa toplamda 101 SQL sorgusu çalıştırılır. Bu durum veritabanı yükünü artırır ve uygulamanın yavaşlamasına neden olur.

İlişkili tabloları tek sorguda getirmek için Include() metodunu kullanabilirsiniz.

Yanlış Kullanım


var siparisler = await context.Siparisler.ToListAsync();

foreach(var siparis in siparisler)
{
    Console.WriteLine(siparis.Musteri.AdSoyad);
}

Doğru Kullanım


var siparisler = await context.Siparisler
    .Include(x => x.Musteri)
    .ToListAsync();

Sonuç: Yüzlerce SQL sorgusu yerine tek sorgu çalıştırılır.

5. İndeks (Index) Oluşturmayı Unutmayın

Veritabanındaki büyük tablolar üzerinde yapılan sorguların hızlı çalışabilmesi için indeks kullanılması oldukça önemlidir. Özellikle WHERE, JOIN ve ORDER BY ifadelerinde kullanılan alanların indeksli olması sorgu süresini ciddi şekilde azaltır.

Entity Framework Core içerisinde Fluent API kullanarak indeksleri kolayca tanımlayabilirsiniz.


protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Personel>()
        .HasIndex(x => x.TCNo);
}

Sonuç: Büyük tablolarda sorgular çok daha kısa sürede tamamlanır.

6. Count() Yerine Any() Kullanın

Bir kaydın var olup olmadığını kontrol ederken birçok geliştirici Count() metodunu kullanmaktadır. Ancak Count() tüm kayıtları sayarken, Any() ilk uygun kaydı bulduğu anda işlemi sonlandırır.

Yanlış Kullanım


bool varMi = context.Personeller
    .Count(x => x.Aktif) > 0;

Doğru Kullanım


bool varMi = await context.Personeller
    .AnyAsync(x => x.Aktif);

Sonuç: Gereksiz kayıt sayımı yapılmadığı için sorgu daha hızlı tamamlanır.

7. SaveChanges() Metodunu Her Döngüde Çağırmayın

Birden fazla kayıt eklerken veya güncellerken her kayıt için ayrı ayrı SaveChanges() çağırmak performansı ciddi şekilde düşürür. Her çağrıda yeni bir transaction başlatılır ve veritabanına bağlantı kurulur.

Yanlış Kullanım


foreach(var personel in liste)
{
    context.Personeller.Add(personel);
    context.SaveChanges();
}

Doğru Kullanım


context.Personeller.AddRange(liste);

await context.SaveChangesAsync();

Sonuç: Tek transaction ile çok daha hızlı kayıt işlemi gerçekleştirilir.

8. Büyük Tablolarda Sayfalama (Pagination) Kullanın

Binlerce hatta milyonlarca kaydı tek seferde ekrana getirmek hem veritabanını hem de uygulamayı gereksiz yere yorar.

Kullanıcılara sadece ihtiyaç duydukları kayıtlar gösterilmeli, diğer kayıtlar sayfalama yöntemiyle yüklenmelidir.


var liste = await context.Personeller
    .OrderBy(x => x.Id)
    .Skip(40)
    .Take(20)
    .ToListAsync();

Sonuç: Daha hızlı açılan ekranlar ve daha az bellek kullanımı.

9. Async Metotları Tercih Edin

ASP.NET Core uygulamalarında senkron çalışan sorgular işlemciyi gereksiz şekilde meşgul eder. Özellikle yoğun trafikli uygulamalarda bu durum ölçeklenebilirliği olumsuz etkiler.

Entity Framework Core'un sunduğu ToListAsync(), FirstOrDefaultAsync() ve SaveChangesAsync() gibi metodlar uygulamanın aynı anda daha fazla isteğe cevap verebilmesini sağlar.


var liste = await context.Personeller.ToListAsync();

await context.SaveChangesAsync();

Sonuç: Daha akıcı çalışan ve daha yüksek kullanıcı kapasitesine sahip uygulamalar.

10. LINQ İçinde Karmaşık İş Mantığı Yazmayın

Entity Framework yalnızca SQL'e çevrilebilen ifadeleri veritabanına aktarabilir. Karmaşık C# metotları veya hesaplamalar LINQ içerisinde kullanıldığında beklenmeyen performans sorunları oluşabilir.

Önce veritabanında filtreleme yapın, ardından uygulama tarafında gerekli hesaplamaları gerçekleştirin.

Yanlış Kullanım


var liste = context.Personeller
    .Where(x => HesaplaMaas(x) > 50000)
    .ToList();

Doğru Kullanım


var personeller = await context.Personeller
    .Where(x => x.Aktif)
    .ToListAsync();

var sonuc = personeller
    .Where(x => HesaplaMaas(x) > 50000)
    .ToList();

Sonuç: SQL sorguları daha verimli çalışır ve Entity Framework gereksiz işlemler yapmaz.

Sonuç

Entity Framework Core, doğru kullanıldığında yüksek performanslı ve geliştirici dostu bir ORM aracıdır. Ancak küçük görünen bazı hatalar, özellikle büyük veri kümeleriyle çalışan uygulamalarda ciddi performans sorunlarına neden olabilir.

Bu makalede ele aldığımız 10 temel öneriyi uygulayarak veritabanı sorgularınızı optimize edebilir, uygulamanızın bellek kullanımını azaltabilir ve kullanıcılarınıza daha hızlı bir deneyim sunabilirsiniz.

Özet

  • ToList() metodunu en sona bırakın.
  • Salt okunur sorgularda AsNoTracking() kullanın.
  • Select() ile yalnızca gerekli alanları çekin.
  • Include() kullanarak N+1 Query problemini önleyin.
  • Sık kullanılan alanlar için indeks oluşturun.
  • Count() yerine Any() metodunu tercih edin.
  • SaveChanges() metodunu tek seferde çağırın.
  • Skip() ve Take() ile sayfalama uygulayın.
  • Async metotları kullanarak uygulamanın ölçeklenebilirliğini artırın.
  • LINQ sorgularını mümkün olduğunca SQL'e çevrilebilir şekilde yazın.

Bu temel prensipleri alışkanlık haline getirerek hem uygulamanızın performansını artırabilir hem de veritabanı sunucusunun gereksiz yük altında kalmasını önleyebilirsiniz.