.NET 8 ile Gelen Keyed Services Dependency Injection Yapısı

Bu yazıda .NET 8 ile gelen ve Keyed Services özelliğinden bahsedilecektir.

Dependency Injection (DI) yapısını hatırlamak gerekirse;

Dependency Injection, projelerimizde bulunan bağımlılıkların yönetebildiği, nesneleri bağımlılıktan kurtaran, değişiklik gerektiğinde minimum değişiklikle düzenleme yapılabilen bir prensiptir.

Temel yaşam süreleri ise; 

Singleton, Scoped, Transient

Konu ile ilgili ".NET Core - Dependency Injection - Oluşturulan Nesnelerin Yaşam Sürelerini Anlamak" başlıklı yazımda detaylarını bahsetmiştim. 

Bugünkü yazıda yaşam sürelerine değil; .NET Core'den beri hayatımızda bulunan Dependency Injection yapısına .NET 8 ile gelen bir özellik olan Keyed Services özelliğine odaklanılacaktır. 

Bağlılıkları isimlendirmek ve aynı servis ismiyle birden fazla key tanımlayarak farklı yerlerde enjekte edilmesi sağlanacaktır.

.NET 8 Öncesi Yaşam Sürelerinin Yönetilmesi

Deneyimlediğim iki farklı yolu bulunuyor. 

İlk seçenek, yaşam süreleri belirlenen bütün servislerden kodun akış sırasına göre en son tanımlanan çalışırdı.

Program.cs'de tanımlanan yaşam süreleri

Controller sınıfında DI kullanımı

Bu görselde SophomoreService en son tanımlandığından, controllerda enjekte edilen de SophomoreService gelecektir. Bu şekilde de sadece tek bir servis kullanılır.

SophomoreService

Loglarda hem FromServices attributesi hem de constructor enjektasyonundan tanımlandığı için Output ekranından log yazılmış oldu. Buradan da SophomoreService sınıfından çağrıldığı belli olmaktadır.

ILogger mesajı

Diğer seçenek, yaşam döngüleriyle ayarlanmış IStudentService ile oluşturulmuş bütün servislerin liste halinde controller constructor metodunda ayarlanarak kullanmak istenilen servisi bulunmasıdır. Listede filtrelemek de ek bir iş olacaktır.

IStudentService türünden listede görünen servisler

Yeni Özellikte Neler Var?

Yeni gelen Keyed servislerle, kullanmak istenilen servisi anahtar bir kelime ile isimlendirdikten sonra, enjekte etmek istenilen yerden bu isimle çağrılabilir.

String bir isimlendirme yapabileceğiniz gibi enum yapıları da kullanabilir. Bu makalede, daha temiz kod anlayışı ile enum yöntemini tercih edeceğim. 

Program.cs dosyasından servislerin yaşam döngüleri, aşağıdaki tabloda karşılaştırılmıştır.

Mevcut Yaşam Döngüleri ve Keyed DI karşılığı



.NET 8 Öncesi KeyedDI Karşılığı
Singleton services.AddSingleton<TService, TImplementation>()
services.AddKeyedSingleton<TService, TImplementation>(object key)

Transient services.AddTransient<TService, TImplementation>()
services.AddKeyedTransient<TService, TImplementation>(object key)

Scoped services.AddScoped<TService, TImplementation>()
services.AddKeyedScoped<TService, TImplementation>(object key)

Örnek Uygulama Hakkında

Örnek bir uygulamada öğrenci tipleri ile ilgili işlemleri yapan sınıflar var. Daha temiz bir key vererek isimlendirme yapmak için StudentType enum yapısı oluşturuldu. Enumdaki her değer, öğrenci tipine karşılık gelmektedir. 
StudentType enum

Program.cs sınıfında AddKeyedTransient olarak uygulamada enjekte edilmek üzere ayarlandı. nameof ile enumların ismi key olarak verildi. (JuniorService için key ismi Junior olacaktır.)
AddKeyedTransient kullanımı

IStudentService interfacesi ise GetStudentId propertysini tutsun. Diğer öğrenci tipleri bu interface'yi miras alacaktır.
IStudentService

SeniorService.cs constructor ile loglama işlemi yapacaktır. Output ekranında yazmasını istiyoruz. Ayrıca tekil bir id değeri de yaratacaktır.
SeniorService.cs

JuniorService, SpecialStudentService, SophomoreService, FreshmanService sınıfları da diğer öğrenci tipleridir.

Servislerin Kullanılması

.NET ekosisteminde IServiceProvider interfacesi ile Dependency Injection yönetimi yapılmakta ve yaşam döngülerini kullanarak oluşturulan servislere buradan ulaşılır. Listede, AddKeyedServices ile eklenen ve isimleri bulunan servisler aşağıdaki gibi görünmektedir.
Keyed Servislerin Provider ile görünmesi

GetKeyedService vs GetRequiredKeyedService

Bu iki metot, provider'de bulunan key ile servisin kullanılmasını sağlar. Parametre olarak key ismi StudentType enum değeri ile verildi. 

Aralarındaki en önemli fark, GetKeyedService'deki isme sahip bir implementasyon yoksa değeri null olabilecekken, GetRequiredKeyedService metodundan ise InvalidOperationException hatası fırlatacaktır.

Yukarıdaki görseldeki hata ise uygulamada "Freshman" keyi ile bir servis tanımlamamış olmamızdan kaynaklıdır. GetRequiredKeyedService, diğerine göre daha katı bir metottur.

FromKeyedServices Attribute

Metodun parametresinde kullanabileceğimiz bir attribute nesnesidir. Key ismi vererek ilgili servis kullanılabilir. Parametre olarak verilen keyed servisinin Program.cs dosyasında tanımlı olması gerekir. 

Loglarda da bu metot çağrıldığında enjekte edilmiş servisler görünmektedir.

Özetle; 

İki versiyon arasında DependencyInjection yapısı kullanım farklarını ve KeyedService özelliğinin uygulanışına değindik.
Yazı boyunca değindiğim bu özelliğin proje geliştirme aşamasında hayat kurtarabilecek bir özellik olduğuna inanıyorum.
Uygulamanın örnek projesine Github Linkinden ulaşılabilirsiniz. Yazının faydalı olmasını umarak çalışmalarınızda kolaylıklar diliyorum :)


Yararlanılan Kaynaklar

Yorumlar