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.

By tanju.bozok

Software Architect, Developer, and Entrepreneur

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir