Ön Hazırlık ve Gereksinimler
Bu rehberi uygulamak için güncel bir geliştirme ortamına ihtiyacınız vardır. Projelerinizi .NET 9 veya .NET 10 (veya üzeri) sürümleri üzerinde yapılandırmanız, modern DI konteyner özelliklerinden tam verim almanızı sağlar. İhtiyacınız olan temel araçlar şunlardır:
- Visual Studio 2022 veya güncel bir IDE (VS Code veya JetBrains Rider).
- .NET SDK (proje türüne göre Web API veya Console Application).
- Katmanlı mimari yapısı için Solution içerisinde ayrılmış Class Library projeleri.
Çalışmaya başlamadan önce, projenizde Microsoft.Extensions.DependencyInjection kütüphanesinin yüklü olduğundan emin olun. Bu kütüphane, .NET dünyasında DI işlemlerini yönetmek için standart haline gelmiş olan yerleşik konteyner yapısını sunar.
Katmanlı Mimaride Dependency Injection Yapılandırması
Katmanlı mimaride en büyük hata, alt katmanların üst katmanları bilmesidir. Bağımlılıklar her zaman üstten alta doğru akmalıdır. DI kullanımı, bu akışı yönetmek için bir "Composition Root" (Bileşim Kökü) gerektirir. Genellikle bu kök, uygulamanın giriş noktası olan Program.cs dosyasıdır.
Aşağıdaki örnekte, bir servis arayüzü ve onun somut uygulamasını DI konteynerine nasıl kaydedeceğinizi görebilirsiniz.
// IProductService.cs - Arayüz tanımlaması
public interface IProductService
{
string GetProductName(int id);
}
// ProductService.cs - Somut uygulama
public class ProductService : IProductService
{
public string GetProductName(int id) => "Örnek Ürün";
}
Bu sınıfları oluşturduktan sonra, bunları Program.cs içerisinde kaydetmeniz gerekir. Bu işlem, uygulamanız ayağa kalkarken gerekli nesnelerin otomatik olarak yönetilmesini sağlar.
// Program.cs - Servis kaydı
var builder = WebApplication.CreateBuilder(args);
// Servisleri konteynere ekleme
builder.Services.AddScoped();
var app = builder.Build();
Service Lifetime (Servis Ömrü) Yönetimi
DI kullanırken en sık yapılan hatalardan biri, servislerin yaşam sürelerini yanlış belirlemektir. .NET içerisinde üç temel yaşam süresi vardır: Transient, Scoped ve Singleton. Bunları doğru seçmek, bellek yönetimi ve performans açısından kritiktir.
| Yaşam Süresi | Açıklama | Kullanım Alanı |
|---|---|---|
| Transient | Her istendiğinde yeni bir nesne oluşturulur. | Hafif ve durum tutmayan servisler. |
| Scoped | Her HTTP isteği (Request) için bir kez oluşturulur. | Veritabanı context'leri, iş mantığı sınıfları. |
| Singleton | Uygulama boyunca tek bir nesne yaşar. | Cache servisleri, konfigürasyon sınıfları. |
Özellikle veritabanı işlemleri için Scoped kullanımı, aynı istek içerisindeki tüm işlemlerin aynı veritabanı bağlantısını paylaşmasını sağlayarak veri bütünlüğünü korur.
Constructor Injection (Kurucu Metot ile Enjeksiyon)
En yaygın ve güvenli DI yöntemi Constructor Injection'dır. Bu yöntemde, bağımlılıklar sınıfın kurucu metoduna parametre olarak verilir. Bu sayede sınıfın hangi bağımlılıklara ihtiyaç duyduğu dışarıdan bakıldığında net bir şekilde görülebilir.
// ProductController.cs - Constructor Injection kullanımı
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var name = _productService.GetProductName(id);
return Ok(name);
}
}
Bu yöntemin en büyük avantajı, ProductController sınıfını test ederken IProductService arayüzünün bir "Mock" (taklit) versiyonunu kolayca enjekte edebilmenizdir.
Katmanlar Arası Bağımlılıkları Çözme
Katmanlı mimaride, örneğin Business katmanınız Data katmanına ihtiyaç duyduğunda, bunu doğrudan referans vermek yerine bir "Service Registration" sınıfı ile yönetmek daha profesyonel bir yaklaşımdır. Her katman kendi servislerini kaydeden bir "Extension Method" barındırmalıdır.
// DataLayer/DependencyInjection.cs
public static class DataLayerExtensions
{
public static IServiceCollection AddDataLayer(this IServiceCollection services)
{
services.AddScoped();
return services;
}
}
Bu sayede Program.cs içerisinde karmaşık kayıtlar yerine sadece builder.Services.AddDataLayer(); komutunu kullanarak tüm katmanı tek seferde sisteme dahil edebilirsiniz.
Güvenlik Uyarısı: Dependency Injection konteynerine hassas verileri (API anahtarları, şifreler vb.) doğrudan kaydetmeyin. Bu tür verileri
appsettings.jsonveya Azure Key Vault gibi güvenli yönetim sistemlerinde tutun veIOptionsarayüzü ile enjekte edin.
Sıkça Sorulan Sorular
Constructor Injection neden Property Injection'dan daha iyidir?
Constructor Injection, sınıfın ihtiyaç duyduğu bağımlılıkları açıkça zorunlu kılar. Property Injection ise bağımlılığın unutulmasına veya null referans hatalarına yol açabilir.
Scoped ile Singleton arasındaki temel fark nedir?
Scoped, tek bir HTTP isteği boyunca yaşar; Singleton ise uygulama çalıştığı sürece bellekte tek bir instance olarak kalır.
DI kullanmak performansı düşürür mü?
.NET'in yerleşik DI konteyneri oldukça optimize edilmiştir. Küçük projelerde fark edilmeyecek kadar düşük bir maliyeti vardır, ancak sağladığı esneklik bu maliyeti fazlasıyla karşılar.
Birden fazla implementasyon varsa nasıl yönetirim?
Konteyner üzerinden tüm kayıtlı servisleri IEnumerable olarak alabilir veya belirli bir implementasyonu ayırt etmek için "Factory Pattern" kullanabilirsiniz.
DI konteynerinde hata ayıklama (debug) nasıl yapılır?
Uygulama başlangıcında IServiceProvider üzerinden servislerin çözümlenip çözümlenmediğini kontrol edebilir, ValidateScopes ayarını aktif ederek yaşam süresi çakışmalarını (örneğin Singleton'dan Scoped çağırma) anında tespit edebilirsiniz.
Dependency Injection ile Birim Testi (Unit Testing) Stratejileri
Katmanlı mimaride Dependency Injection kullanmanın en büyük avantajlarından biri, test edilebilirliği (testability) en üst düzeye çıkarmasıdır. Bağımlılıkları arayüzler (interface) üzerinden enjekte ettiğinizde, gerçek veritabanı veya dış servisler yerine "Mock" nesneleri kullanarak izole birim testleri yazabilirsiniz.
Aşağıdaki örnekte, bir OrderService sınıfının bağımlılığını nasıl sahte (mock) nesnelerle test edebileceğinizi inceleyelim:
// Test sınıfı örneği (Moq kütüphanesi kullanılarak)
[Fact]
public void PlaceOrder_ShouldReturnTrue_WhenInventoryIsValid()
{
// Arrange
var mockInventory = new Mock();
mockInventory.Setup(x => x.CheckStock(It.IsAny())).Returns(true);
var service = new OrderService(mockInventory.Object);
// Act
var result = service.PlaceOrder(101);
// Assert
Assert.True(result);
}
Bu yaklaşım sayesinde, veritabanı bağlantısı kurmanıza veya ağ trafiği oluşturmanıza gerek kalmadan iş mantığınızı (business logic) saniyeler içinde doğrulayabilirsiniz. DI konteyneri, test ortamında gerçek servisler yerine bu mock nesnelerin enjekte edilmesine olanak tanır.
Autofac ile İleri Seviye DI Yapılandırması
Microsoft'un yerleşik Microsoft.Extensions.DependencyInjection kütüphanesi çoğu senaryo için yeterli olsa da, bazen "Assembly Scanning" (otomatik kayıt) veya "Property Injection" gibi daha gelişmiş özelliklere ihtiyaç duyabilirsiniz. Bu noktada Autofac gibi üçüncü taraf kütüphaneler devreye girer.
Örneğin, belirli bir namespace altındaki tüm sınıfları tek seferde kaydetmek için şu yöntemi kullanabilirsiniz:
var builder = new ContainerBuilder();
// "Services" ile biten tüm sınıfları otomatik kaydet
builder.RegisterAssemblyTypes(typeof(MyService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
var container = builder.Build();
Bu yöntem, büyük ölçekli projelerde her servis için tek tek services.AddScoped() yazma zahmetinden sizi kurtarır. Ancak, bu tür otomatik kayıt işlemlerinde "Circular Dependency" (döngüsel bağımlılık) hatalarına karşı dikkatli olmalı ve bağımlılık grafiğinizi düzenli olarak gözden geçirmelisiniz.
Profesyonel İpucu: DI konteynerinizi yapılandırırken her zaman "Composition Root" desenini kullanın. Yani, tüm bağımlılık kayıtlarını uygulamanın en dış katmanında (genellikleProgram.csveyaStartup.cs) yapın ve uygulama içindeki hiçbir sınıfın DI konteynerine (Service Locator deseni gibi) doğrudan erişmesine izin vermeyin.
Sonuç
C# ile katmanlı mimaride Dependency Injection kullanmak, kodunuzu sadece daha temiz hale getirmekle kalmaz, aynı zamanda projenizin gelecekteki değişikliklere karşı direncini artırır. Bu rehberde öğrendiğiniz yöntemleri, kendi projelerinizde küçük adımlarla uygulamaya başlayın. Bir sonraki adım olarak, "MediatR" kütüphanesini inceleyerek CQRS (Command Query Responsibility Segregation) desenini DI ile nasıl entegre edeceğinizi araştırmanızı öneririm.
Sorumluluk Reddi: Bu makalede paylaşılan kod örnekleri eğitim amaçlıdır. Üretim ortamına geçmeden önce tüm bağımlılıklarınızı güvenlik taramalarından geçirin ve DI konteynerinizin yapılandırmasını projenizin özel gereksinimlerine göre optimize edin.


Yorumlar (0)
Yorum Yaz