UZMAN
Interceptors
EF Core'un veritabanı işlemlerine (SaveChanges, komut çalıştırma, bağlantı açma) araya girme mekanizması. Audit logging, soft delete otomasyonu, slow query loglama, query manipulation gibi cross-cutting concern'leri merkezi bir noktada çözer — her repository'ye kod yazmak yerine.
Interceptor Türleri
| Interceptor | Ne Zaman Çalışır | Kullanım Alanı |
|---|---|---|
SaveChangesInterceptor |
SaveChanges öncesi/sonrası | Audit log, soft delete, timestamp |
DbCommandInterceptor |
SQL komutu çalışmadan önce/sonra | Query logging, slow query alert |
DbConnectionInterceptor |
Bağlantı açılma/kapanma | Connection telemetry |
DbTransactionInterceptor |
Transaction başlangıç/bitiş | Distributed tracing |
SaveChangesInterceptor — Audit Log
// Entity'lere ortak interface
public interface IAuditable
{
DateTime CreatedAt { get; set; }
string CreatedBy { get; set; }
DateTime? UpdatedAt { get; set; }
string? UpdatedBy { get; set; }
}
// Interceptor implementasyonu
public class AuditInterceptor : SaveChangesInterceptor
{
private readonly ICurrentUserService _currentUser;
public AuditInterceptor(ICurrentUserService currentUser)
{
_currentUser = currentUser;
}
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken cancellationToken = default)
{
var context = eventData.Context!;
var now = DateTime.UtcNow;
var user = _currentUser.UserId;
foreach (var entry in context.ChangeTracker.Entries<IAuditable>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedAt = now;
entry.Entity.CreatedBy = user;
break;
case EntityState.Modified:
entry.Entity.UpdatedAt = now;
entry.Entity.UpdatedBy = user;
break;
}
}
return base.SavingChangesAsync(eventData, result, cancellationToken);
}
}
DbCommandInterceptor — Slow Query Logger
public class SlowQueryInterceptor : DbCommandInterceptor
{
private readonly ILogger<SlowQueryInterceptor> _logger;
private const int SlowThresholdMs = 500;
public SlowQueryInterceptor(ILogger<SlowQueryInterceptor> logger)
{
_logger = logger;
}
public override ValueTask<DbDataReader> ReaderExecutedAsync(
DbCommand command,
CommandExecutedEventData eventData,
DbDataReader result,
CancellationToken cancellationToken = default)
{
if (eventData.Duration.TotalMilliseconds > SlowThresholdMs)
{
_logger.LogWarning(
"🐢 SLOW QUERY ({Duration}ms): {Sql}",
eventData.Duration.TotalMilliseconds,
command.CommandText);
}
return new ValueTask<DbDataReader>(result);
}
}
Kayıt (Registration)
// Program.cs
builder.Services.AddDbContext<AppDbContext>((sp, options) =>
{
options.UseSqlServer(connectionString)
.AddInterceptors(
sp.GetRequiredService<AuditInterceptor>(),
sp.GetRequiredService<SlowQueryInterceptor>());
});
// DI kaydı
builder.Services.AddScoped<AuditInterceptor>();
builder.Services.AddSingleton<SlowQueryInterceptor>();
Dikkat:
SaveChangesInterceptor→ Scoped (user bilgisi gerekir).DbCommandInterceptor→ Singleton olabilir (stateless ise).