• 29.08.2026 09:29:35
  • Admin Admin

Entity Framework eğitimi kapsamında EF Core sorgularında çeviri önbelleği, veritabanı planı, N+1 ve gereksiz materialization maliyetlerini dotnet-trace, SQL ölçümleri ve derlenmiş sorgularla inceliyoruz.

Entity Framework Eğitimi: EF Core Sorgu Planı ve Derleme Önbelleği

Entity Framework Eğitimi ile iki ayrı önbelleği ayırmak

EF Core sorgu yavaşlığını yalnızca SQL Server tarafında aramak eksik tanıdır. İlk maliyet, LINQ expression tree'nin SQL'e çevrilmesi için EF Core'un tuttuğu sorgu derleme önbelleğidir; ikinci maliyet ise parametreli SQL metninin veritabanındaki yürütme planıdır. Entity Framework eğitimi içinde ikisini ayrı ölçmek gerekir: uygulamada dotnet-trace, SQL Server'da Query Store kullanın. Önce tekrar üretilebilir bir yük çalıştırın:

dotnet tool install -g dotnet-trace
dotnet-trace collect --process-id 12345 --providers Microsoft-Extensions-Logging:4:5
# yük testi için örnek
bombardier -c 32 -n 20000 https://localhost:5001/api/orders?customerId=42
Trace'i PerfView veya dotnet-trace report ile açarken Microsoft.EntityFrameworkCore.Query, RelationalCommand.ExecuteReader ve GC allocation örneklerini aynı zaman aralığında karşılaştırın. Sadece HTTP p95'i değil, istek başına SQL komut sayısını ve allocate edilen bayt miktarını önce-sonra tablosuna yazın.

.net core eğitimi veya .net core kursu içeriğinde sık gözden kaçan hata, her istekte expression ağacına sabit değer gömmektir. Aşağıdaki ilk sürüm, EF'nin expression shape karşılaştırmasında her status değeri için farklı ağaç üretmesine neden olur; ikinci sürüm closure üzerinden parametre kullanır ve aynı shape'i korur. Parametreleşme ayrıca SQL Server'ın parametreli komut üretmesine yardım eder, fakat kötü seçiciliğe sahip parametrelerde plan sniffing sorununu tek başına çözmez.

// Kötü: ConstantExpression her çağrıda farklıdır
var p = Expression.Parameter(typeof(Order), "o");
var body = Expression.Equal(
    Expression.Property(p, nameof(Order.Status)),
    Expression.Constant(status));
var predicate = Expression.Lambda<Func<Order, bool>>(body, p);
var rows = await db.Orders.Where(predicate).ToListAsync(ct);

// İyi: normal LINQ parametreleştirilmiş şekil üretir
var rows = await db.Orders
    .Where(o => o.Status == status)
    .ToListAsync(ct);
EF'nin sorgu derleme davranışını görmek için geliştirme ortamında Microsoft.EntityFrameworkCore.Query kategorisini Debug seviyesine açın; aynı endpoint altında sürekli "Compiling query expression" görüyorsanız expression shape üretimini inceleyin.

C# Eğitimi: sıcak sorgularda EF.CompileAsyncQuery kullanımı

csharp eğitimi ve c# eğitimi örneklerinde derlenmiş sorgu, her LINQ sorgusuna körlemesine eklenecek bir optimizasyon değildir. EF Core zaten query shape önbelleği kullandığından kazanç, çok yüksek frekansta çağrılan, model başına sabit kalan ve uygulama CPU'sunda anlamlı çeviri payı görülen sorgularda ortaya çıkar. Aşağıdaki sorgu statik delegate olarak bir kez derlenir; çağrı anında expression tree karşılaştırması yapılmaz.

public static class OrderQueries
{
    public static readonly Func<SalesDbContext, int, IAsyncEnumerable<OrderSummary>>
        ByCustomer = EF.CompileAsyncQuery(
            (SalesDbContext db, int customerId) =>
                db.Orders.AsNoTracking()
                  .Where(o => o.CustomerId == customerId)
                  .OrderByDescending(o => o.CreatedAt)
                  .Select(o => new OrderSummary(
                      o.Id, o.CreatedAt, o.Total.Amount)));
}

await foreach (var row in OrderQueries.ByCustomer(db, customerId)
                                      .WithCancellation(ct))
{
    yield return row;
}
Ölçümde aynı veri kümesi ve aynı bağlantı havuzu ısınmış durumdayken normal LINQ ile bu delegate'i ayrı ayrı çalıştırın. BenchmarkDotNet'te MemoryDiagnoser ekleyip ortalama süre, Gen0 ve allocation değerlerini raporlayın; sonuçta SQL süresi baskınsa derlenmiş sorgunun farkı istatistiksel olarak anlamsız kalabilir.

c# kursu katılımcılarının üretimde karşılaştığı incelik, derlenmiş sorgu delegate'inin tek bir EF modeline bağlı olmasıdır. Tenant'a göre farklı model oluşturan bir IModelCacheKeyFactory, provider seçenekleri veya aynı süreçte farklı context modeli kullanılıyorsa tek statik delegate yanlış modelde çalıştırılamaz. Ayrıca delegate içine değişken davranış saklamayın: filtre bayrağını parametre yapın.

public static readonly Func<SalesDbContext, int, bool, IAsyncEnumerable<OrderSummary>>
    Search = EF.CompileAsyncQuery(
        (SalesDbContext db, int customerId, bool includeCancelled) =>
            db.Orders.AsNoTracking()
              .Where(o => o.CustomerId == customerId
                       && (includeCancelled || o.Status != OrderStatus.Cancelled))
              .Select(o => new OrderSummary(o.Id, o.CreatedAt, o.Total.Amount)));
Bu ifade tek SQL shape üretir. Ancak SQL Server Query Store'da includeCancelled parametresi için iki farklı cardinality dağılımı kötü plan üretiyorsa, önce gerçek planlardaki tahmin-gerçek satır farkını inceleyin; sorun EF derlemesi değil, veri dağılımı ve indeks olabilir.

ASP.NET Core Eğitimi: sorgu şekli, Include ve materialization maliyeti

asp.net core eğitimi sırasında bir endpoint'in DTO döndürmesi, otomatik olarak dar kolon sorgusu ürettiği anlamına gelmez. Önce entity graph'ı Include ile yükleyip sonra map etmek, tracking snapshot'ları ve kullanılmayan kolonları taşır. Liste endpoint'inde projeksiyonu SQL'e itin, tracking'i kapatın ve üretilen SQL'i ToQueryString() ile doğrulayın.

var query = db.Orders
    .AsNoTracking()
    .Where(o => o.CustomerId == customerId)
    .OrderByDescending(o => o.CreatedAt)
    .Take(100)
    .Select(o => new OrderListItem(
        o.Id,
        o.CreatedAt,
        o.Lines.Sum(l => l.Quantity),
        o.Lines.Sum(l => l.Quantity * l.UnitPrice)));

logger.LogInformation("{Sql}", query.ToQueryString());
var page = await query.ToListAsync(ct);
Önceki Include(o => o.Lines) sürümü ile bu sürüm için SQL Server'da SET STATISTICS IO, TIME ON çalıştırın. Logical reads, CPU time ve dönen kolon sayısını karşılaştırın; p95 düşse bile satır sayısı veya logical read artıyorsa veri hacmi büyüdüğünde regresyon beklenir.

Birden fazla collection navigation içeren Include, tek SQL sorgusunda kartesyen çoğalmaya yol açabilir. Örneğin 20 satır ve 5 not içeren sipariş, join sonucunda 100 satıra genişler; tekrarlanan order kolonları ağ üzerinden gelir ve materializer aynı entity kimliğini ayıklamak zorunda kalır. Bu durumda sorgu sayısı artışı karşılığında veri tekrarını azaltmak için kontrollü biçimde AsSplitQuery() deneyin.

var order = await db.Orders
    .AsNoTracking()
    .Where(o => o.Id == id)
    .Include(o => o.Lines)
    .Include(o => o.Notes)
    .AsSplitQuery()
    .SingleOrDefaultAsync(ct);
Split query, ağ gecikmesi yüksek bağlantılarda üç round-trip oluşturur ve sorgular arasında veri değişirse tutarsız graph döndürebilir. Bu endpoint tutarlı bir anlık görüntü gerektiriyorsa transaction isolation seviyesini veritabanı davranışına göre açıkça belirleyin; bunu tek sorgunun her zaman daha hızlı olduğu varsayımıyla değiştirmeyin.

Microsoft teknolojileri eğitimi için ölçüm ve regresyon kapısı

microsoft teknolojileri eğitimi kapsamında sorgu iyileştirmesini kod incelemesinde doğrulanabilir hale getirin. EF Core komutlarını Activity olarak işaretleyip OpenTelemetry exporter yerine testte bir ActivityListener ile sayın; ayrıca SQL metninde beklenmeyen SELECT *, tekrar eden komut veya OFFSET tabanlı derin sayfalama varsa testi kırın. Üretim telemetry'sinde parametre değerlerini span attribute olarak yazmayın; müşteri e-postası, erişim belirteci ve kişisel veri sızıntısı oluşur.

using var listener = new ActivityListener
{
    ShouldListenTo = source => source.Name == "Microsoft.EntityFrameworkCore",
    Sample = (ref ActivityCreationOptions<ActivityContext> _) =>
        ActivitySamplingResult.AllData
};
ActivitySource.AddActivityListener(listener);

var result = await client.GetAsync("/api/orders/42");
Assert.True(result.IsSuccessStatusCode);
Assert.InRange(sqlCommandCount, 1, 3);
Bu sayacı yalnız entegrasyon testinde etkinleştirin. Test verisi 3 satırsa N+1 sorgusu görünür ama maliyet görünmez; en az bir müşteri için 100 order ve her order için 10 line içeren sabit seed kullanın.

blazor eğitimi yapan ekiplerde özellikle server-side ekranların her render veya filtre değişiminde aynı veriyi sorgulaması yaygındır. UI tarafında sorgu sayısını azaltmak için önce API'nin sayfalama sözleşmesini keyset pagination ile tanımlayın; Skip(page * size) derin sayfalarda SQL Server'ın sıralanmış satırları atlamak için artan miktarda iş yapmasına neden olur. Uygun bileşik indeks, filtre ve sıralama sırasını izlemelidir.

CREATE INDEX IX_Orders_CustomerId_CreatedAt_Id
ON dbo.Orders (CustomerId, CreatedAt DESC, Id DESC);

// Son görülen anahtardan sonraki sayfa
var next = await db.Orders.AsNoTracking()
    .Where(o => o.CustomerId == customerId &&
       (o.CreatedAt < cursor.CreatedAt ||
       (o.CreatedAt == cursor.CreatedAt && o.Id < cursor.Id)))
    .OrderByDescending(o => o.CreatedAt)
    .ThenByDescending(o => o.Id)
    .Take(50)
    .Select(o => new OrderListItem(o.Id, o.CreatedAt, 0, o.Total.Amount))
    .ToListAsync(ct);
Uygulama öncesi ve sonrası için Query Store'da aynı endpoint etiketiyle ortalama duration, logical reads ve plan sayısını karşılaştırın. CreatedAt tek başına eşsiz değilse ikinci anahtar eklemezseniz, aynı zaman damgasındaki kayıtlar sayfalar arasında atlanabilir veya iki kez dönebilir.

İlgili Eğitim

.NET Core Eğitimi

Sık Sorulan Sorular

.net core eğitimi için EF.CompileAsyncQuery ne zaman kullanılmalı?

Önce dotnet-trace veya BenchmarkDotNet ile uygulama CPU'sunda EF sorgu çevirisinin anlamlı pay aldığını gösterin. Aynı modelde, yüksek frekansla çalışan ve sabit LINQ shape'e sahip sorgularda kullanın. Veritabanı yürütme süresi baskınsa, indeks ve plan analizi derlenmiş sorgudan daha önceliklidir.

csharp kursu kapsamında EF Core N+1 sorgusu nasıl ölçülür?

Entegrasyon testinde EF Core DiagnosticSource veya ActivityListener ile DbCommand sayısını endpoint başına sayın. Ardından SQL Server Query Store ya da PostgreSQL pg_stat_statements üzerinden çağrı sayısını doğrulayın. Lazy loading ile 100 siparişin her satırında Lines erişiliyorsa 1 yerine 101 komut görmeniz gerekir.

asp.net core eğitimi sırasında AsSplitQuery her Include için doğru mu?

Hayır. Birden fazla collection Include kartesyen çoğalma üretiyorsa AsSplitQuery ile logical read ve ağda taşınan tekrar eden satırlar azalabilir. Buna karşılık ek round-trip ve sorgular arası tutarsızlık riski oluşur. Tek SQL ve split SQL için STATISTICS IO, TIME çıktısını aynı veri hacminde karşılaştırın.

blazor eğitimi projelerinde EF Core liste ekranı neden yavaşlar?

Derin Skip/Take sayfalaması, sıralanan satırların büyük bölümünü atlatır; render döngüsünde tekrar çağrılırsa maliyet katlanır. CustomerId, CreatedAt ve Id sırasıyla bir bileşik indeks ekleyin, keyset cursor kullanın ve her kullanıcı etkileşiminde üretilen SQL komut sayısını telemetry ile ölçü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