EF Core Performans Taktikleri

EF Core'da performansı artırmak için AsNoTracking ile değişiklik takibini kapatma, ihtiyaç duyulan ilişkileri Explicit Loading ile yükleme ve sık kullanılan sorguları derleyerek cache'leme yöntemleri ele alınır.

EF Core Performans Taktikleri

EF Core Performans Taktikleri: AsNoTracking, Explicit Loading & Compiled Query

Entity Framework Core, büyük bir kolaylık sunar ancak "siyah kutu" gibi davranırsanız, performans uçurumdan yuvarlanır. İşte en kritik 3 taktik ve bilinmeyen püf noktaları:

Taktik 1: AsNoTracking() ile Değişiklik İzleme (Change Tracker) Yükünü Kaldırın

  • Ne İşe Yarar: Varsayılan olarak EF Core, sorgudan dönen her nesneyi ChangeTracker'a ekleyerek (Snapshot veya Proxy mekanizmasıyla) olası bir güncelleme için RAM'de saklar. Bu, özellikle yalnızca okuma (SELECT) işlemlerinde tamamen gereksiz bir CPU ve bellek yüküdür.

  • Kullanım:

    csharp

    var products = await context.Products
                                .AsNoTracking()
                                .ToListAsync();
  • Püf Noktası: Tüm sorgularınız için varsayılan olarak AsNoTracking aktif olsun istiyorsanız, context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; ayarını yapabilirsiniz. Sadece güncelleme yapacağınız sorgularda AsTracking() ile ezebilirsiniz.

  • Performans Kazancı: Büyük veri kümelerinde bellek kullanımını %30-50 oranında azaltır ve GC (Çöp Toplayıcı) üzerindeki baskıyı ciddi oranda düşürür.

Taktik 2: Explicit Loading (Açık Yükleme) ile Gereksiz JOIN'lerden Kaçının

  • Sorun: Include() ile yapılan Eager Loading, her seferinde tüm ilişkili tabloları JOIN'ler. Bu, veritabanına devasa sorgular göndermenize ve veri tekrarına (Cartesian Explosion) neden olur.

  • Çözüm: İlişkili verilere yalnızca ihtiyaç duyulduğunda ve istenen koşullarla gitmek için Explicit Loading kullanın.

  • Uygulama: Ana nesneyi zaten AsNoTracking ile çektiniz diyelim. Sonra kullanıcı detaylara tıkladığında:

    csharp

    // Ana nesne zaten elimde (orders)
    await context.Entry(order)
                 .Collection(o => o.OrderItems)
                 .Query()
                 .Where(i => i.Quantity > 5) // Sadece ağır olan kalemleri getir!
                 .LoadAsync();
  • Kritik Nokta: Explicit loading, Include'a göre daha fazla veritabanı yuvarlak gezintisi (Round-trip) yapar. Bu nedenle yalnızca UI'daki "Detayları Göster" gibi kullanıcı tetiklemeli durumlar için idealdir. API'lerde genelde Select ile projeksiyon yapmak (Taktik 0 gibi düşünün) her zaman daha iyidir.

Taktik 3: Compiled Query (Derlenmiş Sorgular) ile Plan Önbelleğe Alın

  • Sorun: EF Core, her sorgu çalıştığında LINQ ifadelerini SQL'e çevirmek için bir derleme (Compilation) süreci geçirir. Bu maliyet, milyonlarca kez çalışan sorgular için astronomik olabilir.

  • Çözüm: Sorguyu bir kere derleyip, sonraki çağrılarda parametreleri değiştirerek cache'ten (bellekten) çalıştırın.

  • Kullanım (EF Core 6+ ile çok kolaylaştı):

    csharp

    private static readonly Func<MyDbContext, int, Task<Customer>> _getCustomerById =
        EF.CompileAsyncQuery((MyDbContext ctx, int id) =>
            ctx.Customers.AsNoTracking().FirstOrDefaultAsync(c => c.Id == id));
    
    // Kullanımı:
    var customer = await _getCustomerById(context, 42);
  • Püf Noktası: Compiled Query'ler kesinlikle dinamik sorgularla (örneğin koşula göre değişen Where filtreleri) çalışmaz. Yalnızca sabit şablonu (template) olan sorgular için kullanın. Ayrıca AsNoTracking ile birleştirmek ömrünü uzatır.


🚨 Ekstra Bonus: AsSplitQuery() ile Devasa JOIN'lerden Kaçının
EF Core 5+ ile gelen AsSplitQuery(), büyük master-detay (parent-child) ilişkilerinde tek devasa sorguyu parçalara böler. Örneğin 10.000 Order ve her birinin 5 Items'i varsa, normal Include 50.000 satır dönerken, Split Query 2 ayrı sorgu (10.000 + 50.000) gönderir ve veri tekrarını önler. Ancak bunu varsayılan yapmayın; sadece çok büyük veri setlerinde AsSplitQuery() ile aktif edin.

Sonuç ve Altın Kural:
EF Core performansının altın kuralı "İhtiyacın kadarını getir, fazlasını asla" dır.

  • Okuma ise → AsNoTracking() şart.

  • İlişki varsa → Select ile projeksiyon (anonim tip veya DTO) yapmak, tüm bu taktiklerden daha hızlıdır.

  • Sık kullanılan sabit sorgular → CompiledQuery ile taçlandırın.

Bu 3 taktiği uyguladığınızda, EF Core sorgularınız ham ADO.NET performansına inanılmaz derecede yaklaşacaktır.

Tüm yazılar