Dependency Injection (DI) .NET Core'da nasıl çalışır?

Dependency Injection (DI), nesnelerin birbirlerine olan bağımlılıklarını dışarıdan enjekte ederek uygulama esnasında yönetilen ve düzenlenmiş bir kod yapısı sağlar. .NET Core'da DI, bağımlılıkların çözülmesi ve yönetilmesi için gömülü olarak sağlanır, bu sayede uygulama geliştiricilerinin manuel bağımlılık yönetimi yapmasına gerek kalmaz.

Temel DI Kavramları:

  1. Bağımlılık (Dependency): Bir sınıfın işlevini yerine getirmek için ihtiyaç duyduğu başka bir sınıftır. Örneğin, bir OrderService sınıfı, bir EmailService sınıfına ihtiyaç duyuyorsa, EmailService, OrderService için bir bağımlılıktır.
  2. Enjeksiyon (Injection): Bir bağımlılığın bir sınıfa nasıl sağlandığıdır. Bu, genellikle constructor injection ile yapılır.

.NET Core'da DI'nın Çalışma Mantığı

  1. Service Collection: DI ile çalışırken, servisler bir "koleksiyona" eklenir. Bu koleksiyon, uygulamanın başında servislerin tanımlandığı yerdir ve genellikle Startup.cs veya Program.cs dosyasında bulunur.

    public void ConfigureServices(IServiceCollection services)
    {
        // Burada servisleri ekleriz
        services.AddTransient<IEmailService, EmailService>();
        services.AddScoped<IOrderService, OrderService>();
        services.AddSingleton<ILoggingService, LoggingService>();
    }
    

     

  2. Injection Yöntemleri:

    • Constructor Injection: En yaygın kullanılan yöntemdir. Bir sınıfın constructor'ına bağımlılıklar parametre olarak geçilir ve bu bağımlılıklar sınıfa enjekte edilir.

      public class OrderService : IOrderService
      {
          private readonly IEmailService _emailService;
          public OrderService(IEmailService emailService)
          {
              _emailService = emailService;
          }
      
          public void ProcessOrder()
          {
              // Burada email gönderme işlemi yapılır
              _emailService.SendEmail("Order processed successfully");
          }
      }
      

       

    • Method Injection: Bu yöntemde bağımlılıklar bir metodun parametreleri olarak verilir.

      public class OrderService
      {
          public void ProcessOrder(IEmailService emailService)
          {
              emailService.SendEmail("Order processed successfully");
          }
      }
      

       

    • Property Injection: Bağımlılıklar bir property üzerinden enjekte edilir.

      public class OrderService
      {
          public IEmailService EmailService { get; set; }
      
          public void ProcessOrder()
          {
              EmailService?.SendEmail("Order processed successfully");
          }
      }
      

       

Servis Yaşam Döngüleri (Service Lifetimes)

.NET Core'da servislerin ne kadar süre boyunca yaşamasını istediğimizi belirlemek için farklı yaşam döngüleri vardır:

  1. Transient (Kısa Ömürlü): Bu servis her talep edildiğinde yeni bir örnek yaratılır. Genellikle hafif ve stateless servisler için kullanılır.

    services.AddTransient<IEmailService, EmailService>();
    

     

  2. Scoped (İstek Bazlı): Bu servis, bir HTTP isteği süresince aynı örneği kullanır. Özellikle web uygulamalarında, her bir istek için yeni bir örnek yaratılmasına olanak tanır.

    services.AddScoped<IOrderService, OrderService>();
    

     

  3. Singleton (Tek Örnekli): Bu servis, uygulama boyunca sadece bir kez yaratılır ve her yerden aynı örnek kullanılır. Genellikle ağır, kaynak tüketen işlemler için tercih edilir.

    services.AddSingleton<ILoggingService, LoggingService>();
    

     

Örnek Senaryo

Bir e-ticaret uygulaması geliştirdiğinizi varsayalım. Bu uygulamada bir sipariş işlemi gerçekleştirildiğinde, siparişin işlenmesi için bir OrderService kullanıyorsunuz. Aynı zamanda sipariş işlendiğinde kullanıcılara bir e-posta göndermek istiyorsunuz, bu yüzden bir EmailService'e ihtiyacınız var.

  1. Servisleri Tanımlayın:

    public interface IEmailService
    {
        void SendEmail(string message);
    }
    
    public class EmailService : IEmailService
    {
        public void SendEmail(string message)
        {
            Console.WriteLine($"Email sent: {message}");
        }
    }
    
    public interface IOrderService
    {
        void ProcessOrder();
    }
    
    public class OrderService : IOrderService
    {
        private readonly IEmailService _emailService;
        public OrderService(IEmailService emailService)
        {
            _emailService = emailService;
        }
    
        public void ProcessOrder()
        {
            Console.WriteLine("Order processed.");
            _emailService.SendEmail("Order confirmation");
        }
    }
    

     

  2. Servisleri Kayıt Edin: Startup.cs dosyasındaki ConfigureServices metodunda servisleri DI konteynerine ekleyin.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IOrderService, OrderService>();
        services.AddTransient<IEmailService, EmailService>();
    }
    

     

  3. Servisleri Kullanın: Controller'da veya başka bir yerde bu servisleri kullanın:

    public class OrderController : Controller
    {
        private readonly IOrderService _orderService;
    
        public OrderController(IOrderService orderService)
        {
            _orderService = orderService;
        }
    
        public IActionResult CreateOrder()
        {
            _orderService.ProcessOrder();
            return Ok();
        }
    }
    

     

DI'ın Faydaları

  • Gevşek Bağlılık: Sınıflar arası sıkı bağımlılıkları azaltır, böylece sınıfların bağımsız olarak test edilmesini ve değiştirilmesini kolaylaştırır.
  • Kolay Test Edilebilirlik: Mock veya fake bağımlılıklarla test senaryoları oluşturabilirsiniz.
  • Kod Tekrarını Azaltma: Servislerin bir kez tanımlanıp her yerde kullanılmasını sağlar.