Size daha iyi hizmet sunabilmek için çerezleri kullanıyoruz.
Web sitemizde gezinme deneyiminizi geliştirmek, size kişiselleştirilmiş içerik ve hedefli reklamlar göstermek, web sitesi trafiğimizi analiz etmek ve ziyaretçilerimizin nereden geldiğini anlamak için çerezleri ve diğer izleme teknolojilerini kullanıyoruz.
⚠️
KVKK ve Çerez Politikası Bilgilendirmesi
6698 sayılı Kişisel Verilerin Korunması Kanunu (KVKK) ve Aydınlatma Yükümlülüğü kapsamında; web sitemizin temel fonksiyonlarının çalışabilmesi, veri güvenliğinin sağlanması ve performans analizi yapılabilmesi için zorunlu çerezlerin kullanımı gerekmektedir. Çerez kullanımını reddetmeniz halinde, teknik imkansızlıklar ve veri senkronizasyonu kesintileri nedeniyle web sitemizdeki hizmetlerden yararlanmanız mümkün olmamaktadır. Sitemizdeki içeriklere erişebilmek için çerez kullanımını onaylamanız gerekmektedir.
Inversion of Control (IoC) Containers: Bağımlılık Enjeksiyonu (DI) Yaşam Döngüsü Yönetimi
Yazılım mimarilerinde nesne oluşturma ve bu nesnelerin birbirleriyle olan ilişkilerini yönetme süreci, projenin ölçeklenebilirliği ve sürdürülebilirliği açısından kritik bir öneme sahiptir. Modern yazılım geliştirme pratiklerinde, sınıfların kendi bağımlılıklarını kendilerinin oluşturması (tight coupling), kodun test edilebilirliğini zorlaştırır ve esnekliğini azaltır. Bu sorunu aşmak için kullanılan Inversion of Control (IoC) prensibi ve bu prensibin en yaygın uygulama biçimi olan Dependency Injection (DI), nesne yönetimini merkezi bir yapıya devreder.
Şekil 1: Inversion of Control (IoC) Containers: Bağımlılık Enjeksiyonu (DI) Yaşam Döngüsü Yönetimi.
1. IoC ve DI Kavramsal Çerçeve
Inversion of Control (Kontrolün Tersine Çevrilmesi)
Geleneksel programlamada, uygulama akışı ve nesne oluşturma kontrolü geliştiricinin yazdığı kodun elindedir. IoC, bu kontrolün programın kendisinden bir çerçeveye (framework) veya bir konteynere aktarılmasıdır. “Beni arama, ben seni ararım” (Hollywood Principle) felsefesiyle çalışır.
Dependency Injection (Bağımlılık Enjeksiyonu)
DI, bir nesnenin ihtiyaç duyduğu diğer nesneleri (bağımlılıkları) kendi içinde oluşturmak yerine, bu bağımlılıkların dışarıdan “enjekte” edilmesidir. Bu işlem genellikle üç yolla yapılır:
Constructor Injection: Bağımlılıkların sınıfın yapıcı metodu üzerinden verilmesi.
Property (Setter) Injection: Bağımlılıkların public özellikler üzerinden atanması.
Method Injection: Bağımlılığın sadece belirli bir metodun çalışması sırasında parametre olarak geçilmesi.
2. IoC Konteynerlerin Rolü ve Mekanizması
IoC konteynerleri, uygulamadaki tüm bileşenlerin (servislerin) kayıt edildiği ve ihtiyaç duyulduğunda bu bileşenlerin otomatik olarak çözümlendiği (resolve) gelişmiş kütüphanelerdir. Konteyner şu işlemleri yönetir:
Registration (Kayıt): Hangi arayüzün (interface) hangi somut sınıfa (concrete class) karşılık geleceğinin tanımlanması.
Resolution (Çözümleme): Bir nesne talep edildiğinde, onun tüm bağımlılık ağacının taranarak nesnenin oluşturulması.
Lifetime Management (Yaşam Döngüsü Yönetimi): Oluşturulan nesnenin ne kadar süre hayatta kalacağının belirlenmesi.
3. Yaşam Döngüsü (Lifetime) Yönetimi: Teknik Detaylar
Bir servis IoC konteynerine kaydedilirken, bu servisin ne zaman oluşturulacağı ve ne zaman imha edileceği tanımlanmalıdır. Yanlış seçilen bir yaşam döngüsü, bellek sızıntılarına (memory leaks) veya “captured dependency” gibi ciddi mimari hatalara yol açabilir.
3.1. Transient (Geçici)
Her talep edildiğinde yeni bir nesne örneği oluşturulur. Durum (state) tutmayan ve hafif (lightweight) servisler için idealdir.
Kullanım Alanı: Veri doğrulama sınıfları, yardımcı (helper) metodlar.
Performans: Sık nesne oluşturma maliyeti vardır ancak bellek yönetimi daha güvenlidir çünkü nesne işi bitince GC (Garbage Collector) tarafından temizlenir.
3.2. Scoped (Kapsamlı)
Bir “scope” (genellikle bir HTTP isteği) boyunca tek bir nesne örneği oluşturulur. İstek tamamlandığında nesne imha edilir.
Kullanım Alanı: Veritabanı bağlamları (Entity Framework DbContext), kullanıcı oturum bilgileri.
Önemli Not: Asenkron işlemlerde veya arka plan görevlerinde (Background Tasks) scope yönetimine dikkat edilmelidir.
3.3. Singleton (Tekil)
Uygulama başladığında bir kez oluşturulur ve uygulama kapanana kadar aynı nesne örneği kullanılır.
Kullanım Alanı: Cache yönetimi, konfigürasyon ayarları, loglama servisleri.
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Kayıt İşlemleribuilder.Services.AddTransient<ITransientOperation, Operation>();
builder.Services.AddScoped<IScopedOperation, Operation>();
builder.Services.AddSingleton<ISingletonOperation, Operation>();
var app = builder.Build();
Servislerin Tüketimi
Bir Controller içinde bu servisleri çağırdığımızda, her HTTP isteğinde Transient id’nin değiştiğini, Scoped id’nin istek boyunca sabit kaldığını, Singleton id’nin ise uygulama yeniden başlatılana kadar hiç değişmediğini gözlemleriz.
5. İleri Düzey IoC Kütüphaneleri ve Özellikleri
Modern yazılım dünyasında popüler olan bazı IoC konteynerleri şunlardır:
Autofac: .NET dünyasında en çok tercih edilen üçüncü taraf kütüphanedir. Module bazlı kayıt, Property Injection ve Intercept (AOP) desteği ile öne çıkar.
Ninject: Okunabilirliği yüksek (fluent interface) ancak performans olarak biraz daha ağırdır.
Castle Windsor: Olgun ve kurumsal projelerde yaygın kullanılan, oldukça esnek bir yapıya sahip konteynerdir.
StructureMap: .NET’in en eski DI kütüphanelerinden biridir (artık yerini Lamar’a bırakmıştır).
Autofac ile Gelişmiş Kayıt Örneği
Autofac, standart .NET DI konteynerinden farklı olarak “Auto-activation” veya “Circular Dependency” çözümü gibi özellikler sunar.
En sık yapılan hatalardan biridir. Daha uzun ömürlü bir nesneye (Singleton), daha kısa ömürlü bir nesnenin (Scoped veya Transient) enjekte edilmesidir.
Sonuç: Scoped nesne, Singleton içinde “hapsolduğu” için imha edilemez ve uygulama boyunca yanlış state ile yaşamaya devam eder. Bu durum veritabanı bağlantı hatalarına (zombi bağlantılar) neden olur.
Service Locator Anti-Pattern
IServiceProvider‘ı doğrudan kodun içine enjekte edip nesneleri oradan manuel olarak çözmek (GetService<T>) bir anti-pattern’dir. Bu durum, sınıfın bağımlılıklarını gizler ve birim test yazmayı imkansız hale getirir. Her zaman Constructor Injection tercih edilmelidir.
7. IoC Konteynırlarında Performans ve Optimizasyon
IoC konteynerleri çalışma zamanında (runtime) yansıma (reflection) kullanarak nesne üretir. Büyük projelerde binlerce sınıfın çözümlenmesi performans kaybına neden olabilir. Bunu optimize etmek için:
Compiled Expressions: Modern konteynerler (örneğin DryIoc veya Lamar), hızlı nesne üretimi için ifade ağaçlarını (expression trees) derler.
Lazy Initialization: Bağımlılığın sadece ihtiyaç duyulduğu anda oluşturulması için Lazy<T> kullanımı teşvik edilmelidir.
AOT (Ahead-of-Time) Compilation: Özellikle mobil ve bulut tabanlı uygulamalarda, çalışma zamanı yansıması yerine derleme zamanı kod üretimi sağlayan kütüphaneler (örneğin MediatR ile entegre yapılar) kullanılabilir.
Teknik Notlar:
Disposable Nesneler: Konteyner, oluşturduğu nesne IDisposable arayüzünü uyguluyorsa, yaşam döngüsü sonunda Dispose() metodunu otomatik olarak çağırır. Singleton nesnelerin dispose edilmesi ancak uygulama kapanırken gerçekleşir.
Validation: Uygulama ayağa kalkarken ValidateScopes özelliği aktif edilerek, yaşam döngüsü uyuşmazlıkları (Captured Dependency) henüz çalışma aşamasında tespit edilebilir.
8. Sonuç
IoC konteynerleri ve Bağımlılık Enjeksiyonu, modern yazılım mimarisinin yapı taşlarıdır. Bağımlılıkların doğru yönetilmesi, kodun birim testlere (Unit Testing) uygun hale gelmesini sağlar ve sınıflar arası sıkı bağımlılığı ortadan kaldırır. Ancak Transient, Scoped ve Singleton gibi yaşam döngüsü türlerinin yanlış kullanımı, tespit edilmesi zor performans sorunlarına ve hatalara yol açabilir. Yazılım mimarları, projeye uygun kütüphaneyi seçerken sadece popülariteye değil, kütüphanenin sunduğu yaşam döngüsü yönetim yeteneklerine ve performans çıktılarına odaklanmalıdır.