Entity Framework Core: Interceptors Kavramı

Murat Dinç
Devops Türkiye☁️ 🐧 🐳 ☸️
4 min readFeb 11, 2024

Selamlar,

Uzun bir süre iş yoğunluğu nedeniyle makale yazmaya ara vermiş olsam da, yıl sonu döneminin sona ermesiyle birlikte yazılarıma kaldığım yerden devam etme fırsatı buldum. Bu başlangıcı da EF Core: Interceptors ile yapmak istedim.

Entity Framework, bir ORM olarak kendi işine fazla karıştırtmaz. Bu yaklaşımı, bazı kısıtlamalar getirerek kendini güvence altına alsa da, biz geliştiriciler için zaman zaman zorlayıcı durumlar yaratıyor. İşte tam da böyle bir durumda, her sorguda araya girip istenileni uygulanması konusuna ihtiyaç duyduğunuz bir kod yazarken Interceptors kavramıyla karşılaşacaksınız.

Interceptors Nedir?

Interceptors, EF Core’un sağladığı bir mekanizmadır ve temelde, EF Core’un veritabanı işlemlerini daha esnek bir şekilde yönetmesine olanak tanır. Bu işlevsellik, veritabanı komutlarının, bağlantıların ve işlemlerin yaşam döngülerine müdahale etmeyi mümkün kılar. Bu sayede, geliştiriciler uygulamanın veritabanı etkileşimlerini daha detaylı bir şekilde kontrol edebilirler.

Interceptors Tipleri

- DbCommandInterceptor

DbCommandInterceptor, EF Core’un veritabanı komutlarına müdahale etmek için kullandığı bir interceptordur. Bu interceptor sayesinde, SQL sorgularını, komutları ve sonuçlara müdehale edebiliriz.

Metodlar:

  • ReaderExecuting ve ReaderExecuted: Sorgunun okunması işlemlerinden önce ve sonra çalışır.
  • NonQueryExecuting ve NonQueryExecuted: Etkilenen satır sayısı döndürmeyen komutlar için kullanılır.
  • ScalarExecuting ve ScalarExecuted: Tek bir değer döndüren sorgular için uygundur.

- DbConnectionInterceptor

DbConnectionInterceptor, veritabanı bağlantıları üzerinde kontrol sağlar. Bu interceptor ile connection açma, kapatma ve exception handling gibi olaylara müdahale edebiliriz.

Metodlar:

  • ConnectionOpening ve ConnectionOpened: Bağlantının açılması işlemleri için kullanılır.
  • ConnectionClosing ve ConnectionClosed: Bağlantının kapatılması işlemleri için kullanılır.
  • ConnectionFailed: Bağlantı kurulamadığında tetiklenir.

- DbTransactionInterceptor

DbTransactionInterceptor, veritabanı işlemleri üzerinde kontrol sağlamak için kullanılır. Bu interceptor sayesinde, transaction başlatma, commit veya rollback işlemlerine müdahale edebiliriz.

Metodlar:

  • TransactionStarted ve TransactionCommitted: İşlem başlatma ve onaylama işlemleri için kullanılır.
  • TransactionRolledBack: İşlemin geri alınması durumunda çalışır.

Ben popüler bir kaç örnekleme yaparak size konuyu örnekler ile aktarmaya çalışacağım 👇🏻

1. Güvenlik için Interceptor

SecurityInterceptor belli bir tabloya erişimi kontrol ediyor. Eğer belirli bir koşul karşılanmazsa, bir Security Exception fırlatılır.

public class SecurityInterceptor : DbCommandInterceptor
{
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
if (command.CommandText.Contains("SensitiveTable"))
throw new SecurityException("Access to SensitiveTable is restricted.");

return base.ReaderExecuting(command, eventData, result);
}
}

2. Logging için Interceptor

LoggingInterceptor EF Core tarafından gerçekleştirilen her SQL sorgusunu konsola yazdırıyor. İsterseniz, ILogger implementasyonunu ekleyerek loglama işlemini gerçekleştirebilirsiniz.

public class LoggingInterceptor : DbCommandInterceptor
{
public override void ReaderExecuting(DbCommand command,
CommandEventData eventData,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
Console.WriteLine($"Query: {command.CommandText}");
base.ReaderExecuting(command, eventData, interceptionContext);
}
}

3. Performans için Interceptor

PerformanceInterceptor yavaş sorguları tespit ederek, bunları loglamak için kullanılır.

public class PerformanceInterceptor : DbCommandInterceptor
{
public override async Task<DbDataReader> ReaderExecutingAsync(DbCommand command,
CommandEventData eventData,
DbCommandInterceptionContext<DbDataReader> interceptionContext,
CancellationToken cancellationToken)
{
var start = DateTime.Now;
var result = await base.ReaderExecutingAsync(command, eventData, interceptionContext, cancellationToken);
var duration = DateTime.Now - start;

if (duration.TotalSeconds > 5) // 5 saniyeden uzun süren sorgular
Console.WriteLine($"Long Running Query: {command.CommandText} - Duration: {duration.TotalSeconds} seconds");

return result;
}
}

4. Caching için Interceptor

CachingInterceptor sorgu sonuçlarını belleğe almak için kullanılabilir.

public class CachingInterceptor : DbCommandInterceptor
{
private static readonly Dictionary<string, object> queryCache = new Dictionary<string, object>();

public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
var queryKey = command.CommandText;
if (queryCache.TryGetValue(queryKey, out var cachedResult))
return InterceptionResult<DbDataReader>.SuppressWithResult((DbDataReader)cachedResult);

var reader = base.ReaderExecuting(command, eventData, result).Result;
queryCache[queryKey] = reader;
return result;
}
}

5. Zaman Damgası için Interceptor

TimestampInterceptor veritabanına kaydedilen her bir nesneye otomatik olarak zaman damgası ekler.

public class TimestampInterceptor : SaveChangesInterceptor
{
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData,
InterceptionResult<int> result)
{
foreach (var entry in eventData.Context.ChangeTracker.Entries())
{
if (entry.State == EntityState.Added || entry.State == EntityState.Modified)
entry.Property("LastUpdated").CurrentValue = DateTime.Now;
}

return base.SavingChanges(eventData, result);
}
}

Interceptor Kullanımında Oluşabilecek Problemler

- Performans Düşüklüğü

Interceptor’ların en sık karşılaşılan yan etkilerinden biri, performans düşüklüğüdür. Her ne kadar Interceptor’lar veritabanı işlemlerini incelemek ve özelleştirmek için güçlü araçlar olsa da, her bir işlemde ekstra işlemler yapılması sistemin genel performansını olumsuz etkileyebilir. Özellikle, yüksek trafikli uygulamalarda her bir veritabanı çağrısında ekstra loglama veya güvenlik kontrolleri gibi işlemler ciddi bir gecikmeye sebep olabilir.

- Mantıksal Hataların Artması

Interceptor’ların fazla kullanımı, uygulamanın genel akışında beklenmeyen davranışlara ve mantıksal hatalara yol açabilir. Örneğin, bir DbCommandInterceptor kullanılarak SQL sorguları dinamik olarak değiştirildiğinde, bu değişiklikler bazı durumlarda veritabanı bütünlüğünü bozabilir veya beklenmedik sonuçlar doğurabilir.

- Bakım Zorlukları

Interceptor’lar, kodun okunabilirliğini ve bakımını zorlaştırabilir. İş mantığı farklı katmanlara yayıldığında, uygulamanın akışını takip etmek ve hata ayıklamak zorlaşır.

💡 Özellikle, birden fazla Interceptor kullanıldığında, hangi Interceptor’ın ne zaman ve nasıl tetiklendiğini anlamak karmaşık hale gelebilir.

- Test Edilebilirliğin Azalması

Interceptor kullanımının aşırıya kaçması, uygulamanın test edilebilirliğini de olumsuz etkiler. Unit testleri yazarken, Interceptor’ların etkileşimleri dikkate alınmalı ve her bir senaryo için ayrı ayrı testler geliştirilmelidir. Bu durum, test sürecini karmaşıklaştırır ve ekstra çaba gerektirir.

Makaleyi faydalı bulduysanız takip ederek destek olabilirsiniz 🙏

Bir sonraki yazıda görüşmek üzere 😊

--

--