EFEF Core Handbook

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).

Interceptor Akış Diyagramı

SaveChangesAsync SavingChangesAsync Interceptor Audit? Added CreatedAt/By set Modified UpdatedAt/By set SQL