1. Dependency Injection (DI) Nedir? Temel Prensipler
Tanım: DI, bir sınıfın ihtiyacı olan bağımlılıkları dışarıdan (constructor, property veya method) almasını sağlar.
IoC (Inversion of Control): Kontrolün sizin kodunuzdan framework’e devredilmesidir.
Neden Kullanmalı?
- Test Edilebilirlik: Mock nesneler kolayca enjekte edilir.
- Esneklik: Gerçek implementasyonu runtime’da değiştirebilirsiniz.
- Sorumluluk Ayrımı (SRP): Sınıflar yalnızca tek bir işe odaklanır.
2. .NET Core Yerleşik DI Container
2.1. Servis Kayıt Türleri
Transient
- Her talepte yeni bir instance oluşturulur.
- Kullanım: Stateless servisler (logger, DTO’lar).
services.AddTransient<ILogger, FileLogger>();
Scoped
- Her HTTP isteği boyunca aynı instance kullanılır.
- Kullanım: DbContext, kullanıcı oturumu.
services.AddScoped<IUserService, UserService>();
Singleton
- Uygulama ömrü boyunca tek bir instance.
- Kullanım: Cache, konfigürasyon yöneticileri.
services.AddSingleton<ICacheService, MemoryCacheService>();
2.2. Servis Çözümleme Yöntemleri
-
Constructor Injection (Tavsiye edilen)
public class ProductController : Controller
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
}
-
Method Injection (Nadiren)
public class OrderService
{
public void ProcessOrder(IPaymentService paymentService)
{
paymentService.Process();
}
}
-
Property Injection (Dikkatli)
public class ReportService
{
[FromServices]
public ILogger Logger { get; set; }
}
3. Üçüncü Parti DI Container’lar
3.1. Autofac
Avantajlar: Modüler kayıt, decorator desteği, assembly tabanlı otomatik kayıt.
Örnek – Logging Decorator:
public class LoggingModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ProductService>()
.As<IProductService>()
.InstancePerLifetimeScope();
builder.RegisterDecorator<LoggingDecorator, IProductService>();
}
}
public class LoggingDecorator : IProductService
{
private readonly IProductService _inner;
private readonly ILogger _logger;
public LoggingDecorator(IProductService inner, ILogger logger)
{
_inner = inner;
_logger = logger;
}
public void AddProduct(Product product)
{
_logger.Log("Ürün ekleniyor...");
_inner.AddProduct(product);
}
}
3.2. SimpleInjector
Avantajlar: Performans, strict constructor injection, lifestyle validation.
Örnek – Aspect Logging:
public class LoggingInterceptor : IInterceptor
{
private readonly ILogger _logger;
public LoggingInterceptor(ILogger logger) => _logger = logger;
public void Intercept(IInvocation invocation)
{
_logger.Log($"{invocation.Method.Name} çağrıldı.");
invocation.Proceed();
}
}
// Kayıt
container.Register<IProductService, ProductService>();
container.InterceptWith<LoggingInterceptor>(type => type == typeof(IProductService));
4. İleri Düzey DI Senaryoları
4.1. Factory Pattern ile Dinamik Servis Oluşturma
public interface IServiceFactory
{
T CreateService<T>() where T : IService;
}
public class ServiceFactory : IServiceFactory
{
private readonly IServiceProvider _provider;
public ServiceFactory(IServiceProvider provider) => _provider = provider;
public T CreateService<T>() => _provider.GetRequiredService<T>();
}
// Kullanım
var paymentService = factory.CreateService<IPaymentService>();
4.2. Çoklu Implementasyonları Yönetme
services.AddTransient<INotificationService, EmailNotificationService>();
services.AddTransient<INotificationService, SmsNotificationService>();
public class NotificationManager
{
private readonly IEnumerable<INotificationService> _services;
public NotificationManager(IEnumerable<INotificationService> services)
{
_services = services;
}
public void SendAll(string message)
{
foreach (var svc in _services)
svc.Send(message);
}
}
4.3. Conditional Registration
if (env.IsDevelopment())
{
services.AddTransient<IDatabaseService, MockDatabaseService>();
}
else
{
services.AddTransient<IDatabaseService, RealDatabaseService>();
}
5. DI ile Test Stratejileri
5.1. Unit Test’lerde Mock Kullanımı
[Fact]
public void AddProduct_ShouldCallLogger()
{
var mockLogger = new Mock<ILogger>();
var service = new ProductService(mockLogger.Object);
service.AddProduct(new Product());
mockLogger.Verify(logger => logger.Log(It.IsAny<string>()), Times.Once);
}
5.2. Integration Test ile TestServer
public class IntegrationTests
{
private readonly TestServer _server;
private readonly HttpClient _client;
public IntegrationTests()
{
_server = new TestServer(WebHost.CreateDefaultBuilder()
.UseStartup<TestStartup>());
_client = _server.CreateClient();
}
[Fact]
public async Task GetProducts_ReturnsSuccess()
{
var response = await _client.GetAsync("/api/products");
response.EnsureSuccessStatusCode();
}
}
public class TestStartup : Startup
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDatabaseService, MockDatabaseService>();
base.ConfigureServices(services);
}
}
6. DI Anti-Pattern’ler
- Service Locator: var svc = HttpContext.RequestServices.GetService<IProductService>();
- Constructor Over‐Injection: 10’dan fazla bağımlılık alan sınıflar.
- Captive Dependency: Scoped servisi Singleton içinde kullanmak.
7. Performans Optimizasyonları
- Transient vs Singleton: Hafif vs ağır nesne seçimi.
- Lazy Loading:
public class HeavyService
{
private readonly Lazy<IExpensiveResource> _resource;
public HeavyService(Lazy<IExpensiveResource> resource) => _resource = resource;
public void Execute() => _resource.Value.DoSomething();
}
8. Gerçek Dünya Senaryoları
8.1. E-Ticaret Uygulaması
public class OrderProcessor
{
public OrderProcessor(IPaymentService payment, IInventoryService inv, INotificationService notify) { … }
public void ProcessOrder(Order order)
{
payment.Process(order.Total);
inv.UpdateStock(order.Items);
notify.Send($"Sipariş #{order.Id} tamamlandı.");
}
}
8.2. Microservices Mimarisinde DI
services.AddOcelot()
.AddSingleton<ICacheService, RedisCacheService>()
.AddTransient<ILogger, CloudLogger>();
services.AddConsulServiceDiscovery(cfg => cfg.Address = new Uri("http://consul:8500"));
9. DI ve Diğer Araçlar
-
AutoMapper Entegrasyonu
services.AddAutoMapper(typeof(Startup));
-
FluentValidation Entegrasyonu
services.AddScoped<IValidator<Product>, ProductValidator>();
10. Sonuç ve En İyi Uygulamalar
- Constructor Injection varsayılan tercihiniz olsun.
- Service Lifetimes’ı doğru seçin.
- Anti-Pattern’lerden kaçının (Service Locator, Captive Dependency).
- Mock & TestServer ile kapsamlı test stratejisi oluşturun.