ASP.NET Core Dependency Injection

2 4.960

Merhaba,

Bu yazıda, son yılların popüler teknik terimlerinden olan Dependency Injection kavramını
ASP.NET Core üzerinde detaylı örneklerle anlatmaya çalışacağım.

Makale İçeriği

  • Loose Coupling yaklaşımı ile refactoring
  • DI Constructor Injection
  • DI Container ile Service registration
  • Dependecy Injection
  • Inversion Of Control ( IoC )
  • Dependency Inversion Principle
  • xUnit Test senaryosu

Dependency Injection Nedir ?

Kısaca DI olarak adlandırdığımız bu yapı NetCore framework’ün temel ilkelerinden birisidir.  Türkçemize “Bağımlılık Enjeksiyonu”  adıyla  çevrilmiştir.

Asıl anlatılmak istenen ise; farklı nesnelerin birbirleriyle olan bağlılıklarını daha esnek bir yapıda ele alıp oluşabilecek yazılım maliyeti ve karmaşanın önüne geçmektir.
Dependency Injection tasarım deseni iki temel prensip üzerine kurulmuştur.

  1. Inversion Of Control ( IoC )
  2. Dependency Inversion Principle

Inversion Of Control

Inversion Of Control ( IoC ) ;  Temel olarak uygulamanızda nesne oluşturma aşamalarının sizin yerinize framework’ün yönettiği yapıdır.  IoC genel bir kavramdır ve nesnelerin farklı tekniklerle Inject edilebilmelerine olanak sağlar.
Dependency Injection ise IoC’nin uygulama yöntemlerinden biridir. IoC tarafında ayaklandırılmış nesnenin  nereye inject edileceğine nokta atışı karar verilen yapıdır.

 

 

Neden Dependency Injection Kullanılır ?

Problem1 : Bağlılık Yapısı
Problem2 : Test Edilebilirlik

Proje büyüdükçe maliyet ve karmaşa artar bu nedenle yapılar olabildiğince sade, okunaklı ve anlaşılabilir hazırlanmalıdır. Yaşam döngüsü boyunca bir çok geliştirme hatta yapısal değişikliğe uğrayan projelerde kurduğumuz yapıların değişikliklerden minimum ölçüde etkilenmesi  gerekir. Bahsedilen bu hedefi yakalayabilmek için  loosely coupled ( Esnek bağlılık ) yaklaşımını sergileyebilmek önemli, işte tam bu noktada Dependency Injection pattern ihtiyaça yönelik yaklaşımı sunar.
Ek olarak uzun vadede Bug Fix maliyetlerinin azaltılmasında önemli rol oynayan test senaryolarının doğru ve efektif yazılabilmesi için de önemli ölçüde  DI ihtiyacı vardır.

Problemin Tanımlanması

 

Örnek Senaryo ( iş kuralları için kodlama yapılmamıştır)
Index sayfasında Ziyaretçiler için Ürün önermesi ( ProductSuggestion ) yapılacak.

Ürün önerisinde bulunacak sınıfı aşağıdaki gibi hazırladım.

 

HomeController/Index
Anasayfada ürün önerimizi gösterebilmek için Index action methodunu tanımlayalım

Yukarıdaki kod blogunu incelediğimizde HotelController sınıfının Index methodunda GuestProductSuggestion sınıfını new keyword ile oluşturduğunu görüyoruz. HomeController GuestProductSuggestion sınıfından instance almayla sorumlu olduğuna göre aralarında                 tam bağımlılık oluşmuş durumdadır.

 

Örnek Senaryo için Ek Geliştirme ; Index sayfasında Kayıtlı kullanıcı için farklı iş kuralları ve ürün önermesi oluşturulmalıdır.

 

Örnek senaryomuza yeni bir gelitşirme eklendi, kayıtlı kullanıcılar için farklı ürün önermeleri yapılacak. Bahsedilen kullanıcılarr için öneri dönüşü yapan yeni bir sınıf oluşturmamız gerekmektedir.

FuneralCoffinDance

 

( Eğer bu aşamada soyutlama yerine if koşullandırması veya parametre ile ayrıştırma yoluna giderseniz ilerde sizi iyi şeyler beklemiyor )  🙂    >>>>

 

 

 

Aşağıdaki gibi UserProductSuggestion sınıfını oluşturalım.

 

HomeController Index methodununu hatırlayalım. Kayıtlı kullanıcılar için geçerli bir tahminleme yapmak istiyorsak aşağıdaki kod bloğunu değiştirmeniz gerekiyor ve bu değişiklik farklı kod bloklarını da etkileyebilir.. İşte bu karmaşa bizim ilk problemimiz bkz;
[ Problem1: Bağlılık Yapısı ]

 

Şimdi ikinci problemi tanıyalım, Ürün önerme Testleri için 2 farklı senaryo hazırladım. ( Test işlemleri için xUnit kullanılmıştır )

 

1. Test senaryosu ; ürün önerisinin olduğu senaryoyu test ediyor.

2. Test senaryosu ; Herhangi bir ürün önerisinin olmadığı senaryoyu test ediyor.

 

Testleri çalıştırdığımızda 1. Test senaryosunun başarılı olduğunu görüyoruz. 2. Test ise hata veriyor.
Methodu biraz daha açıklamaya çalışayım ;

  • Home Controller üzerinden instance oluşturuldu.
  • Index methodu Invoke edildi.
  • Methodunda sonucu return değerine ulaşıldı. [ Core.DI.Model.IndexModel ]
  • Model dönüşü üzerinden test result oluşturuluyor

 

Ürün önerisinin başarılı olmadığı test senaryosunu inceleyelim;

Hatırlatma için Index action kodlarını ekledim. GuestProductSuggestion nesnesinden instance almak ve yönetmekten sorumlu olduğundan HomeController > Index ile GuestProductSuggestion  arasında tam bağımlılık vardı

 

Tam bağımlılık nedeniyle test için uygulanan senaryoların tamamında GetProductSuggestion methodu aynı değeri döndürecektir. ( ProductList = new List<string> { “Klavye”, “Kulaklık” } )  işte bu durum bizim ikinci problemimizi ortaya koyuyor bkz;
[ Problem2 : Test Edilebilirlik ]

Çözümü Tasarlayalım

 

Problemi tanımlayabildiğimize göre artık çözüm yoluna geçebiliriz. Kod tasarımında bazı değişiklikler yapmalıyız. Loose Coupling yaklaşımı ile tam bağımlılıkları ortadan kaldıracağız. Yüksek seviyedeki sınıfların düşük seviyedeki sınıflara olan bağlılığını ters bağlılığa çevireceğiz. Bunun için soyutlaştırma kullanacağız ve constructor Injection dan yararlanacağız. Inversion Of Control ile dışarıdan Inject edilen sınıfların instance yönetimini uygulamamızdan çıkartıp, üst seviyede ele alacağız. ( Service Registration )

 

Dependency Injection Tersine Bağlılık

 

Dependency Inversion Principle; Yüksek seviyeli sınıfların düşük seviyeli sınıflara direk bağımlı olmadan yönetilmesini hedefleyen prensiptir. Problemin karşılaşıldığı yapılarda bağımlılığın soyutlandırma ile tersine çevirilmesi şeklinde yorumlanabilir.

 

Problem1: Bağlılık Yapısı

Öncelikle HomeController sınıfının GuestProductSuggestion sınıfına bağımlılığını ortadan kaldıralım.
Soyutlandırma yönteminden faydalanacağımız için GuestProductSuggestion sınıfını Interface ile dışarıya açmamız gerekiyor.

IProductSuggestion inteface’ini oluşturalım.

 

Ürün önerisini yapacak olan sınıflarrımızı IProductSuggestion Interface’inden türetelim.

 

Artık HomeController’ı bağlılıktan kurtarmanın vakti geldi.Dependency Injection ile HomeController’a IProductSuggestion’ı inject edelim ve Index Action’ı düzenleyelim.

 

Index action methoduna bakacak olursak new ile Instance almadığımızı Dependency Injection üzerinden Constructor Injection ile gelen değeri kullandığımızı görebilirsiniz.

Inversion Of Control ile IProductSuggestion interface’imizi uygulamanın kullanabilmesi için hazır bir instance haline getirelim.

Startup.cs > ConfigureServices

 

Problem2: Test  Edilebilirlik

Test için yazdığımız kodlarda ufak değişiklikler yapacağız.  Daha önce hata veren test methodumuzu aşağıdaki gibi değiştirdim.
Bağlılığımız dışarıdan Inject edildiği için Interface üzerinde Mock ile Controller test senaryoma uygun örneği oluşturabiliyorum.

 

Umarım faydalı bir yazı olmuştur.

 

 

 

 

Inversion Of Control Discussion
Inversion Of Control Matin Fowler Article

 

 

 

 

2 Comments
  1. Ilker Karan says

    Merhaba Burak,

    Yazilarini buyuk keyifle takip ediyorum. Bu ise yeni baslayan genc arkadaslara yonlendirme konusunda Turkce kaynak sikintisini terimleri yok etmeden gidermek bence buyuk emek! Konunun anlatimiyla ilgili nacizane elestirim olacak. DIP genel itibariyle o kadar yuzeysel ve Dependency Injection ve IoC ile karistirilarak anlatiliyor ki bu prensipin asil noktasini kacirdigimizi dusunuyorum. Bu yazida da bu fark anlatilmamis. Giris seviyesindeki bir yazi icin ne kadar onemli ya da karisikliga sebep olur mu bilemiyorum ama DIP konusu gectiginde bunun altinin cizilmesi gerektigini dusunuyorum.

    Yukarida yazdiklarim kesinlikle yazinin icerdigi yararli bilgiyi es gecme cabasi degildir. Yazi basit orneklerle acik sekilde DI ve loosly-coupled yaklasimlarini tarifliyor. Emegine saglik kardesim, yaptigin seyi imrenerek takip ediyorum.

    Keep Safe!
    Ilker

    1. H.Burak Karadağ says

      Selam İlker,
      Öncelikle bu güzel yorumun ve eleştirin için teşekkür ederim. Senden bu yorumu ve eleştiriyi almak beni ayrıca memnun etti. Aktarmaya çalıştığım konunun bir çok örneğinde DI ve IoC kavramlarının çok karışık ve iç içe anlatıldığını ön görüp bir nebze bu iki kavramı yazıda ayrıştırmayı hedeflemiştim. DIP için söylediklerine tamamen katılıyorum. Malesef konudan çok kopmamak için bir iki cümleyle tanımını yapmak ve
      çok kısa uygulanabilirliğini örnekleme yolunu seçtim.
      En kısa zamanda SOLID çerçevesinde DIP ile ilgili daha detaylı bir yazı hazırlayıp makale içerisine ekleyeceğim.

Email adresiniz yayınlanmayacaktır.