ASP.NET MVC Projesinde Dependency Injection Uygulanması

30 Haz

Daha önce hazırladığım Dependency Injection Tasarım Deseni başlıklı yazıda, yazılım tasarımında sınıfların birbirine olan bağımlılıklarını esnetmeyi ve bağımlılıkların sınıf dışından enjekte edilmesini incelemiştik. Bu yazımızda ise konunun bir örnek uygulaması niteliğinde olan ASP.NET MVC uygulamalarında Dependency Injection uygulamasını inceleyeceğiz.

Bu yazımızdaki örnek uygulamamızı geliştirirken, soyut sınıflar ile somut sınıflar arasındaki bağlantıyı kurarken ve somut nesne oluştururken IoC Container denen yardımcı kütüphanelerden faydalanacağız. Yine bu örnek uygulamada IoC Container kütüphanesi ile  somut nesnelerin kullanıcı(client) sınıf içerisinde “new” anahtar sözcüğü ile oluşturmak yerine, IoC Container tarafından oluşturulduğunu göreceğiz.

Örnek Uygulama

Örnek uygulamamızda ASP.NET MVC controller sınıfına Dependency Injection tarasım deseni ile bağımlılıkları dışarıdan soyut nesneler(interface veya abstract) yardımıyla vererek uygulamayı çalıştırmayı deneyeceğiz.

Öncelikle Controller sınıfımızı tanımlayarak işe başlayabiliriz.

public class HomeController : Controller
{
    private readonly IProductService productService;

    public HomeController(IProductService productService)
    {
        this.productService = productService;
    }
    public JsonResult Index()
    {
        IEnumerable<Product> products = productService.GetBestSellers();

        return Json(products, JsonRequestBehavior.AllowGet);
    }

}

Gördüğünüz üzere, Controller sınıfında hiç bir şekilde nesne oluşturma işlemi gerçekleştirilmiyor. Dışarıda oluşturulan ProductService nesneleri Controller sınıfı içerisine enjekte ediliyor. IProductService tipi bir interface’dir. Bu interface tipini uygulayan sınıflar HomeController içerisinde çalışabilir. IProductController tipindeki nesneler constructor metodu yardımıyla sınıf içerisine alınıyor.

ASP.NET MVC Controller sınıflarının varsayılan(default) halleri çalışma zamanında parametresiz constructor metodlarını ararlar. Bu tanımlama ASP.NET DefaultControllerFactory içerisinde belirlenmiştir. ASP.NET MVC open source olduğundan isteyen MVC kodlarını açıp inceleyebilir. Biz constructor seviyesinde injection işlemi yapacağımızdan DefaultControllerFactory sınıfının GetControllerInstence metodunu ezerek yani yeniden tanımlayarak istediğimiz formata getirmeliyiz.

Bu tanımlamayı yapmadan önce uygulamada kullanacağımız NInject kütüphanesinden bir iki cümle ile bahsedelim. Ninject kitüphanesi bir konteynır olarak çalışır. Soyut nesneler ile somut nesnelerin eşleştirildiği ve istendiğinde somut nesnelerin(new) oluşturulduğu kütüphanelerdir.

public class NinjectControllerFactory : DefaultControllerFactory
{
     private readonly IKernel kernel;

     public NinjectControllerFactory()
     {
         kernel = new StandardKernel(new NinjectBindingModule());
     }

     protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
     {
         return controllerType == null ? null : (IController)kernel.Get(controllerType);
     }
}

public class NinjectBindingModule : NinjectModule
{
     public override void Load()
     {
         Kernel.Bind<IProductService>().To<ProductService>();
     }
}

Burada tanımlanan kod parçasında anlatılmak istenen şey DefaultControllerFactory metodunun Controller Instance oluştururken karşılaşacağı parametresiz constructor metodu oluşturma hatasını engellemektir. Ninject aracılığı ile Controller tipine göre bir nesne oluşturuyor. Örneğin HomeController için controllerType HomeController olacaktır ve constructor metodunda IProductService interface tipini parametre olarak alacaktır. HomeController tipinde bir nesne oluştururken constructor metodda karşılaştığı parametreyi Kernel bildiği için hemen nesnesini oluşturarak Controller sınıfına vermektedir.

Artık Global.asax sınıfı içerisine bu yapıyı tanıtma işlemi kaldı. Onu da şu şeklide gerçekleştirebiliriz.

public class MvcApplication : System.Web.HttpApplication
{
     protected void Application_Start()
     {
        //diğer tanımlamalar...

        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
     }
}

Uygulamayı çalıştırdığımızda çıktı olarak şu şekilde bir JSON veri dönecek:

[{"Name":"Notebook"},{"Name":"PC"}]

Örnek uygulamanın kodlarını SkyDrive üzerinden paylaştım. Bir sonraki yazıda görüşmek üzere.

Kaynak Kodlar: Burada

10 Replies to “ASP.NET MVC Projesinde Dependency Injection Uygulanması

  1. Hocam elinize sağlık. Yanlız birçok açık kaynak mvc projesi inceledim, hepsinde controller için dependency injection kullanılmış. Ama hiç birisinde de kullanılma avantajı sağlanmamış. Yani ilerde belki ihtiyaç olurda eklenir diye eklenmiş. Bu konuda fikriniz nedir? Gerçekten lazım olacağı bir örnek verebilir misiniz?

    Mesela IProductService productService i kullanacak başka servisler olabilir mi? Örneğin incelediklerimden en iyilerinden birinde yaklasık 40 tane servis interface var ve bunlara ait 40 tane de sınıf var. Yani anlayacağınız her interface için tek sınıf kullanılmış (hatadan dolayı degil, ihtiyac olmadıgından kullanılmamıs)

    En azından kullanılsa iyi olur diyebileceğiniz bir örnek var mı? Benim aklımda bir tane var. Mesela IMessagingService gibi bir interface den, SmsMessagingService ve MailMessagingService gibi iki sınıf türetebiliriz. Ama gerçek hayatta kullanılacak olan bir örnek için çok büyük bir onem tasımıyor. Bir blog sitesi için mesela, kaç tane aynı sınıftan türemiş nesne olacak. (controller dependecy injection için konusuyorum)

    Biraz uzun ve karışık oldu sanırım. Bilmiyorum anlatabildim mi?

    Saygılar….

  2. Ben anladığımı söyleyeyim, yanlış varsa düzeltirsin. Sanırım anlatmak istediğin şey, servis niyetli oluşturulan interface tiplerinin her birinden tek bir sınıf türetilmiş olması, halbuki bu interface tiipinden farklı sınıflar da üretilip kullanılabilir olacağı ve neden kulanılmadığı şeklinde.

    Dependency injection kullanmanın avantajı bu örnek için controller sınıfında “new” anahtar kelimesiyle ProductService üretilmekten kutulmak
    ve “new” anahtar sözcüğü ile üretilen somut nesnenin dışardan alınmasını sağlamaktır. Örnekte nesne oluşturma işlemini de Ninject denen kütüphane yapıyor. Bir diğer avantajı da Controller sınıfının testi sırasında kolaylık sağlamasıdır.

    Senin örneğindeki IMessagingService tipini kullanan bir constructor olduğunu varsayalım. Şu anda üyelere Email ile bilgilendirme yapıyorsanız Ninject MailMessagingService nesnesini oluşturup controllere gönderecek, bir süre sonra SMS ile bilgilendirme yapmaya karar verdiğinizde Ninject MailMessagingService nesnesini üretip gönderecektir.Bu işlem için Web.config dosyasına koyacağınız bir ayar ile kodu etkilemeden
    runtime esnasında web.config içindeki ayarı değiştirerek işleyişe etki edebilirsiniz.

    Tabi controller parametrelerini arttıracaksanız haliyle kodda bir iki basit değişiklik yapmanız gerekebilir.

  3. Hocam Ninject container kullanmak zorunda mıyız? Bir de web API için nasıl çalışır bu mekanizma?

    • Elbette Ninject kullanmak zorunda değilsiniz. Piyasada .Net için yazılmış bir çok IoC container Mevcuttur.
      Bunlar: Castle Widsor, StructureMap, Spring.Net, Autofac, Unity diye devam eder gider. WebAPI için yakında ayrı bir makale hazırlayacağım. Takip edebilirsin.

  4. Merhaba Hocam,

    “Bu işlem için Web.config dosyasına koyacağınız bir ayar ile kodu etkilemeden
    runtime esnasında web.config içindeki ayarı değiştirerek işleyişe etki edebilirsiniz.”

    demişsiniz, fakat web.config değiştiği anda application’ımız zaten restart olmaz mı?

    • Application neden restart olsun ve restart olsa bile konu ile ne alakası var. Örneğin web.config dosyasında şöyle bir ayar olsun
      " < add key="Messaging" value="Mail" / "
      Bu durumda uygulama Mail gönderecektir.
      " < add key="Messaging" value="SMS" / "
      ise SMS gönderecektir.

  5. İlk yorumu ben yazmıştım ama isim yazmayı unutmuşum. Bildirimde gelmedi cevabınızı şimdi gördüm.

    Sorumu, farklı sorayım. DI’ nin somut olarak sunduğu avantaj nedir? DI kullanmadan neyi başaramıyoruz ve DI kullanırsak ne gibi bir avantajımız olur. “new” anahtar kelimesinden kurtuluyoruz demişsiniz, evet bu konuda haklısınız ama “new” kelimesinden kurtulmanın tam olarak avantajı nedir? Zira “new” kelimesini kullanmanın mimariye, ne gibi bir külfeti olabilir?

    ikinci olarak test aşamasında kolaylık demişsiniz, buna somut bir örnek verebilirmisiniz? Yani DI kullandığımızda ve kullanmadığımızda ne oluyor?

      • Bu yazıdaki örnek dışında bir örnekle cevap vereyim. Örneğin MessageService şeklinde bir sınıfınız var. Kullanıcılara mesaj gönderiyor olsun. MessageService sınıfı mesajı email şeklinde göndermek için EmailSender isimli bir sınıfı kullanıyor olsun. Yarın bir gün SMS ile mesaj göndermek isteyeceksiniz. SMSSender diye bir sınıf olşuturup MessageService sınıfına vermemiz gerekecek. SMSSender ve EmailSender tiplerini MessageService ana sınıfına bir şekilde enjekte etmemiz gerekiyor. Aksi taktirde MessageService sınıfı içerisine EmailSender ve SMSSender tiplerine ait nesneleri direkt olarak tanımlamamız gerekecek. Bu istenen bir durum değildir. Çünkü her yeni mesaj tipi için MessageSender ana sınıfı içine bir tanımlama yapmamızı gerektirecektir. Bunu yapmak yerine nesneleri dışardan enjekte etmek, MessageSender tipini değiştirmeye gerek kalmadan kullanmaya olanak sağlayacak.

  6. Merhaba, Yukarıdaki alirıza beyin yorumuna katılıyorum. Tam olarak neden kullanmalıyız.

Comments are closed.