Dependency Injection / Ioc

Inversion of Control Container

Ioc Container

Uygulamaların IoC Container yapısını kurmak için Ninject ve Autofac kütüphaneleri kullanılıyor.

Ninject

Ninject paketi kullanılır. Uygulamanın tipine göre Ninjectin ek paketleri kullanılıyor.ASP.NET MVC

  • Ninject.MVC5

  • Ninject.Web.Common

  • Ninject.Web.Common.WebHost

  • Ninject.Web.Common.OwinHost (OWIN kullanılıyorsa)

WCF

  • Ninject.Extensions.Wcf

  • Ninject.Web.Common

  • Ninject.Web.Common.WebHost

Autofac

Autofac paketi kullanılır. .NET Core projelerinde Dependency Injection yapısına Autofac'i entegre etmek için Autofac.Extensions.DependencyInjection paketi kullanılır.

Ioc yapısının merkezileştirilmesi (Composition Root)

Ioc modülleri (NinjectModule, Autofac.Module) ve DependencyResolver sınıfları için ProjeAdı.Ioc projesi kullanılır ve uygulamaların Ioc tanımları bu projede yapılır. Uygulama projesinde ve diğer projelerde Ioc ile ilgili dizinler, dosyalar olmaz.

Sınıfların Bağımlılıkları

Sınıf bağımlılıkları Construction Injection kullanılarak yapılır. Yani bir sınıfın kullanacağı diğer bileşenlerin arayüzleri ilgili sınıfın yapıcı metoduna parametre olarak geçirilir ve bileşeni temsil eden member değişkene atama yapılır.

Setter (Property) Injection ve dolayısıyla [Inject] (Ninject) etiketinin kullanılması önerilmez.Setter injection kullanımının dezavantajları:

  • Sınıfın bağımlılıklarının görünür olmasını engeller, sınıf bağımlılıklarının asgaride tutulması hedefini zedeleyecek bir ortam yaratır.

  • Sınıfları Ninject kütüphanesine bağımlı kılar. IoC Container yapısı değiştirilirse ve başka bir araç kullanılırsa bu sınıfların güncellenmesi gerekir.

InRequestScope

Ninject

Ninjectin sınıf oluşturma davranışı incelendiğinde bazı genel kullanılan Framework sınıflarının yüzlerce adette oluşturulduğu görüldü. Bu durumu engellemek için bu sınıflarda InRequestScope ayarı kullanıldı ve her istekte sadece bir örneğin oluşturulması sağlandı. Not: InRequestScope ayarı Ninject.Web.Common.WebHost paketinde yer alır ve Ninject.Web.Common isim uzayında bulunur.

  • ILogger

  • ILoggerWrapper

  • ISettingsService

  • IConfigurationManagerWrapper

  • ISecurityService

Autofac

Her HTTP isteğinde bir sınıfın bir örneğinin oluşturulup kullanılması için Autofac'ta InstancePerLifetimeScope scope tanımı kullanılır.

Singleton

Uygulama çağında bir sınıfın örneğin bir kere oluşturulup her istekte bu örneğin kullanılması için Singleton scope tanımı kullanılır.

Ninject

kernel.Bind<IAutoMapper>()
.To<Data.AutoMapper>()
.InSingletonScope();

Autofac

builder.RegisterType<NugetService>()
.As<INugetService>()
.SingleInstance();

Sınıf bağımlılıklarında dikkat edilmesi gerekenler

  • Birden fazla sorumluluğu olan büyük sınıfların birbirlerine bağımlı olması, yapıcı metotlar ile başlayan nesne oluşturma işleminde performans kaybına neden oluyor. Bir web metodunun çalışabilmesi için 2-4 saniye geçebiliyor.

  • Web projelerinde Controller bağımlılıklarından başlayarak, oluşturulacak object graphın derinlik seviyesi az tutulmalı.

  • Single Responsibility Principle gözetilerek sınıflar sorumluluklarına göre parçalanmalı ve yeni sınıflar oluşturulmalı. Controllerlar da aynı şekilde parçalanmalı.

  • Bir sınıfın içerdiği member servislerin kullanımları kontrol edilerek, tespit edilen anlamlı gruplara göre yeni sınıflar oluşturulmalı. Daha önceden büyük olan ana sınıf da küçültülmeli.

  • Bir sınıfın sadece bir veya birkaç metodunu kullanan istemci servis için bu metotları içeren daha küçük sınıflar oluşturulmalı.

  • Sınıfların bağımlı olduğu sınıfların veritabanına veya veri kaynağına en kısa yoldan erişen sınıflar olması tercih edilmeli. Sınıfların object graphlarındaki derinlik seviyesi olabildiğince düşürülmeli.

ServiceFactory kullanımı (Ninject)

Bir sınıfta az kullanılan bir memberın bağımlılığını yapıcı metottan vermek yerine bir ServiceFactory aracılığıyla, gerektiği anda oluşturulmasını sağlayacak bir yapı kullanılabilir. Bunun nedeni bu az kullanılan memberı oluşturmanın getireceği object graph yükünü azaltmak. Bu memberın oluşturulması maliyetli ise ve kullanıldığı yerdeki metot da bir şekilde başka bir sınıfa taşınamıyorsa bu yöntem kullanılabilir. Ama çok sık kullanılması önerilmez. (Aslında bu member'ın az kullanıması sınıfın low-cohesion'a sahip olduğunu gösterir ve bu konu ele alınmalı, refactoring düşünülmelidir.)

Bunun için Ninject.Extensions.Factory kütüphanesi ve yeni bir Bind ifadesi kullanılır.

kernel.Bind<IProfilePrivacyServiceFactory>().ToFactory();

Az kullanılan sınıf yerine bu sınıfın Factory’si yapıcı metotta bağımlılık olarak belirtilir.

public ProfileSummaryService(IprofilePrivacyServiceFactory profilePrivacyServiceFactory)
{
    this.profilePrivacyServiceFactory = profilePrivacyServiceFactory;
}

Factory arayüzü:

public interface IProfilePrivacyServiceFactory
{
    IProfilePrivacyService CreateInstance();
}

Kullanımı:

var profilePrivacyService = profilePrivacyServiceFactory.CreateInstance();

Bu arayüzün implementasyonunu çalışma zamanında Ninject oluşturuyor ve bunu mevcut kernel üzerinden yapıyor.

https://github.com/ninject/ninject.extensions.factory/wiki

Convention tabanlı Ioc tanımları

Arayüzlerin hangi implementasyonları oluşturacağı tanımını tek tek yapmak yerine, belli isimlendirme ve isim uzayı kuralları ile toplu Bind işlemi yapılabilir.

Ninject

Bu işlem Ninject'te Ninject.Extensions.Conventions paketi kullanılarak yapılır.

Örnek: Aşağıdaki tanım MemberService sınıfının isim uzayında bulunan ve isimleri Service ifadesi ile biten sınıfların arayüzleri ile implementasyonlarını bağlar.

kernel.Bind(x => x.FromAssemblyContaining<MemberService>()
.SelectAllClasses()
.InNamespaceOf<MemberService>()
.EndingWith("Service")
.BindAllInterfaces()
.Configure(y => y.InRequestScope()));

Autofac

builder.RegisterAssemblyTypes(typeof(Business.Factories.FileSystemFactory).Assembly)
.Where(t => t.Name.EndsWith(ClassSuffixNames.Factory))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();

Last updated