• 1.11.2026 07:39:09
  • İbrahim Gökyar

EF Core sorgu anti-pattern'lerini, dotnet-trace/dotnet-counters ile profillemeyi ve BenchmarkDotNet ölçümlerini içeren pratik .net core eğitimi yazısı. C# örnekleri ve ölçülebilir önce-sonra sonuçlarıyla uygulamalı .NET optimizasyonu.

EF Core ile Gerçek Dünya Sorgu Optimizasyonu: .net core eğitimi için uygulamalı rehber

Veri erişim profillemesi: dotnet-trace, dotnet-counters ve PerfView kullanımı

Profilleme adımı olarak öncelikle dotnet-counters ve dotnet-trace ile temel metrikleri toplayın: CPU, GC allocation, threadpool ve exception rate. Somut komutlar:

dotnet-counters ps
# pid öğrenildikten sonra
dotnet-counters monitor -p 12345 System.Runtime --refresh-interval 1000
# uzun trace için
dotnet-trace collect -p 12345 -o myapp.nettrace --providers Microsoft-Diagnostics-NetCore-EventSource:0:5
Bu komutlarla talep altında allocation/saniye, gen0/1/2 sayıları ve CPU kullanımını ham olarak elde edersiniz; PerfView veya dotnet-trace convert araçlarıyla .nettrace dosyasını açıp GC root ve allocation stacklerini inceleyin.

Opendart Akademi

Bu konuyu uygulamalı öğrenmek ister misiniz?

.NET Core Eğitimi eğitimimizde canlı projelerle, eğitmen eşliğinde adım adım ilerliyorsunuz.

Eğitimi incele →Ücretsiz danışmanlık: WhatsApp

Önemli noktalar: EF Core ile Gerçek Dünya Sorgu Optimizasyonu:...: Veri erişim profillemesi: dotnet-trace, dotnet-counters ve PerfView kullanımı; Önce problemsel bir örnek: N+1 oluşturan bir sorgu ve ChangeTracker source olayı.; BenchmarkDotNet ile mikrobenchmark: repository ve sorgu karşılaştırması; Kötü DbContext lifetime kullanımı bellek sızıntılarına ve thread contention'a yol açar.; Ölçülebilir değişiklik raporlama ve yaygın hatalar - deneyimli geliştirici notları
Önemli noktalar: EF Core ile Gerçek Dünya Sorgu Optimizasyonu:...

Ölçüm metodolojisi: 1) Kontrol listesi ve test senaryosu hazırlayın (ör. aynı endpoint'e 10k istek -c 50). 2) Yük aracını çalıştırın:

hey -n 10000 -c 50 http://localhost:5000/api/orders
3) Aynı anda dotnet-counters ile metrikleri kaydedin, sonra dotnet-trace ile 30-60s trace toplayın. 4) Değişiklik uygulayıp (ör. AsNoTracking, projection) aynı testleri tekrarlayın. Karşılaştırılacak metrikler: p95 response time, GC allocations MB/s, gen0 collections/s ve CPU%. Bu somut akış sayesinde her optimizasyonun mekanik etkisini görürsünüz.

EF Core sorgu anti-pattern'leri ve somut C# kod optimizasyonları

Önce problemsel bir örnek: N+1 oluşturan bir sorgu ve ChangeTracker source olayı. Bu kod çok sık görülen bir anti-pattern olup bir istek başına çok sayıda SQL roundtrip ve büyük allocation üretir:

var orders = await db.Orders
    .Where(o => o.CreatedAt > since)
    .Include(o => o.Items)
    .ToListAsync();

foreach (var o in orders)
{
    var customer = await db.Customers.FirstOrDefaultAsync(c => c.Id == o.CustomerId); // N+1
}
Profil sonucu: dotnet-counters'ta allocation/saniye yüksek, dotnet-trace'te çok sayıda SQL ExecuteReader call stack görünür.

İyileştirilmiş versiyon: projection + AsNoTracking + derlenmiş sorgu (compiled query) ile hem SQL roundtrip'lerini azaltın hem de ChangeTracker yükünü kapatın. Örnek:

// Projeksiyon ve AsNoTracking
var ordersDto = await db.Orders
    .AsNoTracking()
    .Where(o => o.CreatedAt > since)
    .Select(o => new OrderDto { Id = o.Id, Total = o.Items.Sum(i => i.Price) })
    .ToListAsync();

// Derlenmiş sorgu (EF Core compiled query)
static readonly Func>> _compiled =
    EF.CompileAsyncQuery((AppDbContext ctx, DateTime s) =>
        ctx.Orders.AsNoTracking()
           .Where(o => o.CreatedAt > s)
           .Select(o => new OrderDto { Id = o.Id, Total = o.Items.Sum(i => i.Price) }));

var result = await _compiled(db, since);

// Toplu güncelleme için ExecuteUpdate (EF Core destekliyorsa)
await db.Orders.Where(o => o.Status == "Old")
    .ExecuteUpdateAsync(s => s.SetProperty(o => o.Status, _ => "Archived"));
Bu değişikliklerin mekanizması: AsNoTracking azaltır ChangeTracker nesne tahsisini; projection sorgunun column set'ini küçültür ve EF'nin nesne örnekleme maliyetini kaldırır; compiled query JIT/plan maliyetini amortize eder.

Nasıl doğrulayacaksınız: benchmark veya yük testi çalıştırdıktan sonra dotnet-counters ve dotnet-trace ile tekrar ölçün. Örnek ölçüm raporu (örnek değerler): p95 latency: 180ms -> 38ms ; gen0 alloc/s: 12MB/s -> 2.1MB/s. Bu somut önce-sonra karşılaştırması yanlış pozitifleri (ör. ağ gecikmesi) elemeye yarar.

BenchmarkDotNet ile mikrobenchmark: repository ve sorgu karşılaştırması

Mikrooptimizasyonları net ölçmek için BenchmarkDotNet kullanın; kullanımı basittir ve allocation/GC detaylarını verir. Örnek benchmark sınıfı:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class RepoBench
{
    private AppDbContext _db;

    [GlobalSetup]
    public void Setup() => _db = TestFactory.CreateDbContext();

    [Benchmark]
    public async Task<int> Baseline() => (await _db.Orders.Where(o => o.IsActive).ToListAsync()).Count;

    [Benchmark]
    public async Task<int> Optimized() => (await _db.Orders
        .AsNoTracking()
        .Select(o => o.Id).ToListAsync()).Count;
}

// Program.cs
BenchmarkRunner.Run<RepoBench>();
Projeyi Release modunda çalıştırın:
dotnet run -c Release --no-build -p /path/to/benchmark.csproj
BenchmarkDotNet size Mean, StdDev, Allocated memory gibi kesin metrikler verecek; bunları dotnet-counters çıktıları ile korele edin.

Benchmark kullanırken dikkat: veri erişim için gerçek veritabanı kullanıyorsanız kontrol edilen environment (local SQL, container veya test veritabanı) olmalı; InMemory provider gerçek SQL davranışını yansıtmaz. Mikrobenchmark'lar JIT ve framework etkilerini gösterebilir; gerçek yük testleri (hey/k6) ile entegre ederek uçtan uca etkiyi doğrulayın. Örneğin BenchmarkDotNet sonucu Allocation: 120 KB -> 18 KB ise dotnet-counters ile allocation/saniye düşüşünü doğrulayın.

Makalenin yol haritası: EF Core ile Gerçek Dünya Sorgu Optimizasyonu: .ne…
Makalenin yol haritası: EF Core ile Gerçek Dünya Sorgu Optimizasyonu: .ne…

Uygulama yapılandırması: AddDbContextPool, connection pool ve DbContext lifetime

Kötü DbContext lifetime kullanımı bellek sızıntılarına ve thread contention'a yol açar. Somut yapılandırma örneği:

services.AddDbContextPool(options =>
    options.UseSqlServer(Configuration.GetConnectionString("Default"))
           .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
           .EnableSensitiveDataLogging(false)
);

// connection string örneği: Max Pool Size ekleyin
"ConnectionStrings": {
  "Default": "Server=...;Database=...;User Id=...;Password=...;Max Pool Size=200;"
}
AddDbContextPool, DbContext örneklerini yeniden kullanarak GC yükünü azaltır; ancak dikkat edin, DbContext içinde state (ör. static cache) veya singleton service inject edilmemeli.

Edge case ve uyarılar: 1) AsNoTracking kullanırken güncelleme yapacaksanız önceki kodu bozabilirsiniz - entity üzerinde SaveChanges beklenmiyorsa projection kullanın. 2) AddDbContextPool ile DbContext constructor'ında scoped olmayan servis (ör. singleton ILogger) alırsanız concurrency sorunları çıkabilir. 3) Blazor bağlamı: Blazor Server'da DbContext 'scoped' olmalı; Blazor WebAssembly'de doğrudan SQLite/EF kullanmak yerine API'ye yönlendirip DTO transferi tercih edin (blazor eğitimi notu). Somut test: pooling açmadan ve açtıktan sonra aynı yük testini çalıştırın ve dotnet-counters ile gen0 koleksiyon oranını karşılaştırın.

Ölçülebilir değişiklik raporlama ve yaygın hatalar - deneyimli geliştirici notları

Her optimizasyon adımını değişiklik kontrolüyle (Git commit) bağlayıp, otomatik ölçümle kaydedin: CI'da Release build altında BenchmarkDotNet raporlarını üretin ve prometheus/grafana ile dotnet-counters'tan elde ettiğiniz metrikleri saklayın. Örnek CI adımı (bash):

dotnet test -c Release
dotnet run -c Release -p benchmarks/RepoBench.csproj
# dotnet-counters'i background'da başlatıp sonuçları dosyaya yönlendirin
dotnet-counters monitor -p $PID System.Runtime --refresh-interval 1000 > counters.log &

Yaygın hatalar: 1) Mikrobenchmark sonuçlarını doğrudan API yığınına genellemek (Hangi parça optimize edildiğini net ayırın). 2) EnableSensitiveDataLogging'ı production'da açık bırakmak (performans ve güvenlik maliyeti vardır). 3) AsNoTracking ile entity güncellenmeye çalışılması; bunun sonucu 'tracked entity required' hatalarıdır. Bu durumlardan korunmak için kod örnekleri, CI benchmark ve dotnet-trace/PerfView izleme kombinasyonunu standartlaştırın.

Öğrendiklerinizi gerçek bir projede uygulayın

.NET Core Eğitimi ile bu makaledeki konuları canlı derslerde, gerçek projeler üzerinde uygulayarak öğrenin. Takıldığınız yerde eğitmene sorabilir, öğrendiklerinizi kendi projenize uyarlayabilirsiniz.

Eğitimi incele →Ücretsiz danışmanlık: WhatsApp

Sık Sorulan Sorular

EF Core sorgu performansını ölçmek için hangi araçları kullanmalıyım (.net core eğitimi kapsamında)?

Somut olarak dotnet-counters (realtime allocation/CPU), dotnet-trace (detaylı .nettrace), PerfView (GC root/heap analiz), ve BenchmarkDotNet (mikrobench sonuçları) kullanın. Örnek akış: yük testi başlatın (hey/k6), sonra

dotnet-counters monitor -p <pid> System.Runtime
dotnet-trace collect -p <pid> -o trace.nettrace
dosyasını PerfView ile açıp allocation stack'lerini inceleyin.

Entity Framework performansını artırmak için C# tarafında hangi somut değişiklikleri yapmalıyım (csharp eğitimi için örnek)?

Somut adımlar: 1) Gereksiz ChangeTracker maliyetini kaldırmak için AsNoTracking kullanın; 2) Sadece ihtiyacınız olan kolonları alın (Select projection); 3) Sık kullanılan sorguları EF.CompileQuery ile derleyin; 4) Toplu güncellemeler için ExecuteUpdate kullanın. Kod örneği:

var dto = await db.Orders.AsNoTracking().Select(o => new { o.Id, o.Total }).ToListAsync();
// compiled
var q = EF.CompileQuery((AppDbContext c, int id) => c.Orders.AsNoTracking().FirstOrDefault(o => o.Id == id));
var single = q(db, 42);

Bir .net core kursu veya c# kursu sırasında hangi profilleme pratiğini öğretmeliyim ki gerçek dünya sorunlarını saptayabilelim?

Öğrencilere önce yük testi (hey/k6) + dotnet-counters monitor + dotnet-trace collect ile uçtan uca bir iz yakalama öğretilmeli; ardından PerfView ile trace açılıp allocation hot-path'lerinin bulunması gösterilmeli. CI içinde BenchmarkDotNet ile mikrobenchmarkların otomatik koşulması ve değişikliklerin metrike etkisinin raporlanması da dahil edilmelidir. Somut görev: bir endpoint için 'önce' metrikleri alın, kodu AsNoTracking/projection ile değiştirin, tekrar ölçün ve p95 ile allocation farklarını raporlayın.

AI / LLM Discovery

Bu makale Opendart Akademi Microsoft / C# eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt

📚 Bu konuyu Opendart Akademi'de öğrenin: .NET Core eğitimi · .NET Core ReactJS eğitimi hakkında · YouTube video dersleri · Abone ol