> For the complete documentation index, see [llms.txt](https://bekoturko.gitbook.io/yazilim-gelistirme-wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bekoturko.gitbook.io/yazilim-gelistirme-wiki/uygulama-yapisi-ve-katmanlari/dependency-injection-ioc.md).

# Dependency Injection / Ioc

### 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

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

#### Autofac

```csharp
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.

```csharp
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.

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

Factory arayüzü:

```csharp
public interface IProfilePrivacyServiceFactory
{
    IProfilePrivacyService CreateInstance();
}
```

Kullanımı:

```csharp
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.

```csharp
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();
```
