UZMAN
Diagnostics & Profiling
Production'da sorguların ne kadar sürdüğünü, kaç sorgu çalıştığını ve N+1 problemlerini tespit etmek için EF Core diagnostics araçlarını kullan.
1. EF Core Logging Levels
// Program.cs — Development'ta SQL göster
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString)
.LogTo(Console.WriteLine, LogLevel.Information) // Sadece dev!
.EnableSensitiveDataLogging() // Parametre değerlerini göster
.EnableDetailedErrors()); // Detaylı hata mesajları
// Production: sadece warning ve üstü
// appsettings.Production.json
// "Logging": { "LogLevel": { "Microsoft.EntityFrameworkCore": "Warning" } }
2. Query Tags — SQL'de İzlenebilirlik
var products = await context.Products
.TagWith("GetActiveProducts - ProductController.Index")
.TagWith($"UserId: {currentUser.Id}")
.Where(p => p.IsActive)
.ToListAsync();
-- SQL Server Profiler / pg_stat_activity'de görürsün:
-- GetActiveProducts - ProductController.Index
-- UserId: 42
SELECT [p].[Id], [p].[Name], [p].[Price]
FROM [Products] AS [p]
WHERE [p].[IsActive] = 1;
3. ToQueryString() — Debug Aracı
// Query'yi çalıştırmadan SQL'ini gör (debug sırasında)
var query = context.Products
.Where(p => p.Price > 100)
.OrderBy(p => p.Name);
Console.WriteLine(query.ToQueryString());
// Parametre değerleriyle birlikte tam SQL çıktısı verir
4. OpenTelemetry Entegrasyonu
// NuGet: OpenTelemetry.Instrumentation.EntityFrameworkCore
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddEntityFrameworkCoreInstrumentation(options =>
{
options.SetDbStatementForText = true; // SQL'i span'a ekle
options.SetDbStatementForStoredProcedure = true;
})
.AddSource("Microsoft.EntityFrameworkCore")
.AddConsoleExporter()); // veya Jaeger, Zipkin, OTLP
5. N+1 Tespit — Interceptor ile
// N+1 tespiti için EF Core Interceptor (production-safe, derlenebilir)
public class QueryCountInterceptor : DbCommandInterceptor
{
private static readonly AsyncLocal<int> _queryCount = new();
public static int CurrentRequestQueryCount => _queryCount.Value;
public static void Reset() => _queryCount.Value = 0;
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
_queryCount.Value++;
return result;
}
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = default)
{
_queryCount.Value++;
return ValueTask.FromResult(result);
}
}
// Kayıt:
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString)
.AddInterceptors(new QueryCountInterceptor()));
// Middleware ile tespit:
app.Use(async (context, next) =>
{
QueryCountInterceptor.Reset();
await next();
var count = QueryCountInterceptor.CurrentRequestQueryCount;
if (count > 10)
Log.Warning("⚠️ N+1 şüphesi: {Path} → {Count} sorgu",
context.Request.Path, count);
// Header olarak da eklenebilir (dev ortamda)
context.Response.Headers["X-EF-Query-Count"] = count.ToString();
});
Diagnostics Araçları Özet
| Araç | Ne Yapar | Ortam |
|---|---|---|
LogTo() |
EF loglarını konsola yaz | Development |
TagWith() |
SQL'e yorum ekle (izlenebilirlik) | Her ortam |
ToQueryString() |
SQL'i çalıştırmadan gör | Debug |
| OpenTelemetry | Distributed tracing, span'lar | Production |
| MiniProfiler | Web UI'da sorgu sürelerini göster | Development/Staging |
dotnet-counters |
EF runtime metrics | Production |