Dependency Injection (DI) | .NET

DI Nedir?

Özge Odabaş
3 min readDec 7, 2023

Dependency Injection, nesnelerin bağımlılıklarını direkt olarak sınfılar içerisinde oluşturmak yerine external olarak almasına yani “enjekte edilmesine” olanak tanıyan bir tasarım modelidir . Bu yaklaşım, bağımlılıkları farklı implementasyonlarla değiştirme esnekliği sağlayarak kodu daha modüler, bakımı kolay ve yeniden kullanılabilir hale getirir.

Yazılımın farklı kısımlarının birbirine sıkı sıkıya bağlı olmak yerine (tight coupling) daha gevşek bir bağlılık (loose coupling) inşa eder.

Seperation of Concerns

Basit Kullanım

Basit bir örnekle başlayalım. Elimizde bir Account sınıfı ve bir Logger sınıfı olsun. Account sınıfından her nesne üretiminde konsola bir log mesajı yazdırmak isteyelim. Bunu dependency injection kullanmadan basit olarak yaparsak;

public class Account{
public int id;

public Account(int id){
this.id=id;
//Logger sınıfından direkt olarak obje oluşturduk
var logger = new Logger();
logger.Log($"Account with {id} id is created");
}
}

Bu koddaki problem Account sınıfı Logger sınıfında sıkı olarak bağlı olması. Logger sınıfında yapılan bir değişiklik Account sınıfında da değişiklik yapılmasını gerektirebilir.

Bu noktada Dependeny Injection kullanarak daha sağlıklı kod yazabiliriz;

public class Account{
public int id;

public Account(int id, Logger logger){
this.id=id;
//instead of -> var logger = new Logger();
logger.Log($"Account with {id} id is created");
}
}

Bu kodda Logger sınıfını constructor injection yöntemi ile enjekte ettik. Bu sayede Account sınıfının Logger sınıfından bir nesnenin oluşturulması gerektiğini veya nasıl konfigüre edilmesi gerektiğini bilmesine gerek yok.

Bu örnekte gördüğümüz yöntem constructor injection olarak karşımıza çıkıyor. Bunun yanında method injection ve setter injection yöntemleri de sık kullanılan dependency injection yöntemlerindendir.

public class Account{
public int id;

//method injection
public void doSomethingComplex(Logger logger){
//some operations
logger.Log("complex things done");
}
}
public class Account{
public int id;

//setter injection
public Logger logger {get; set;}
}

Interfaceler ile Kullanım

Basit bir uygulama içerisinde yukarıda gördüğümüz şekliyle dependency injection kullanmak uygun görünebilir. Fakat uygulamalarımız büyüdükçe yazılım mimarisi daha önemli hale gelir. Bu noktada DI desenini interface gibi yapılarla güçlendirip kullanmak gerekir.

//interface
public interface ILogger{
void Log(string message);
}

//console logger class that implements ILogger interface
public class ConsoleLogger:ILogger{
public void Log(string message){
Console.WriteLine(message);
}
}

//file logger class that implements ILogger interface
public class FileLogger:ILogger{
public void Log(string message){
File.AppendText(message);
}
}

Account sınıfımızda direkt olarak somut bir sınıf enjekte etmek yerine, interfaceleri enjekte edebiliriz. Bu kullanım bize farklı logger implementasyonları ile çalışabilme esnekliği sağayacak.

public class Account{
public int id;

//use interface instead of concrete classes
public Account(int id, ILogger logger){
this.id=id;

logger.Log($"Account with {id} id is created");
}
}

Dependency Injection kullanımı bize Dependency Inversion prensibini de uygulamamızı sağlıyor.

Dependency Inversion — Kod concrete implementasyonlar yerine abstract yapılara bağımlı olmalıdır.

.NET’de Dependency Injection

.NET’de dependency injection built-in olarak gelir. Genellikle sınıflar ve bağımlılıklar arasında IoC (Inversion of Control) tekniğini uygulamak için kullanılır.

Inversion of Control (IoC)

Bağımlılığın .NET içerisinde built-in service container olarak gelen IServiceProvider’a register olması gerekir. Tüm servisler eklendikten sonra .NET bunlarda bir container oluşturur. Buradan bağımlılıktan bir instance oluşturma ve gerekli olmadığı zaman dispose etme sorumluluğunu framework üstlenir.

Örneğin yukarıda oluşturduğumuz Logger sınıfları için .NET uygulamamızın startup’ında aşağıdaki gibi singleton servis kaydı oluşturup, concrete sınıfların instancelarını DI containerda tutabiliriz.

//other codes
builder.Services.AddSingleton<ILogger, ConsoleLogger>();
//other codes

Bu kayıt sayesinde, uygulama hayatı boyunca ilk requestten itibaren tek bir ConsoleLogger instance’ı üretilip, ILogger enjekte edilen yerde kullanılabilir ve gerek duyulmadığı noktada otomatik olarak dispose edilir hale gelecek.

Duruma göre Singleton dışında Transient (AddTransient) ve Scoped (AddScoped) ile de servis kaydı gerçekleştirebiliriz.

Singleton, Transient, Scoped

Örneğin bir ASP.NET Core uygulamasında Controller, Business ve Data Access katmanları arasında bu yöntemleri kullanıp bir dependency chain oluştururuz. Framework talep edilen bağımlılıkları sırasıyla containerdan resolve ederek gerekli yerde kullanılmalarını sağlar.

Çözülmesi gereken bağımlılıklar kümesine genellikle dependency tree, dependency graph veya object graph denir.

Bu yöntemleri kullandığım ASP.NET Core projemi inceleyip daha fazla şey öğrenebilirsiniz.

https://github.com/ozgeeodabass/EatIt

--

--

Özge Odabaş

Merhaba! Ben Özge. Junior Java Developerım. Kendimi geliştirirken edindiğim bilgileri yazıyorum. Keyifli okumalar.