Asp.Net Core basitçe nedir ne değildir bahsetmiştik daha önceki yazılarımızda. Bugünkü yazıda ise Asp.Net Core için Dependency Injection konusuna değinip framework içerisinde default gelen built-in DI Container'ını inceleyeceğiz.
.Net core için 3rd party DI container'lar kullanılabildiği gibi küçük basit uygulamalar yada microservice'ler için kullanılabilen microsoft'un geliştirmiş olduğu basit kullanıma sahip bir built-in default DI tool'u da framework ile sunulmuştur. Castle, Autofac yada Unity framework'leri kadar çeşitli özelliklere sahip olmasa da oldukça kullanması basit ve performanslı bir framework olarak karşımıza çıkmakta. Örnek bir proje üzerinden nedir, ne değildir, nasıl kullanılır anlatmaya başlayalım.
Creating a Sample Project
Aşağıdaki gibi Asp.Net Core'da Web Api olarak yazılmış bir projemiz olsun ve proje içerisinde Repository ve Service katmanları için kullandığımız interface'leri container'a register edelim ve kullanalım. İlk olarak domain objemizi aşağıdaki gibi yaratalım.
namespace AspCoreDIContainerSample.Domain
{
public class City
{
public Guid Id { get; set; }
public string Name { get; set; }
}
}
Sonrasında yukarıda oluşturduğumuz domain model için repsoitory inteface ve sınıfını oluşturalım.
namespace AspCoreDIContainerSample.Repository
{
public interface ICityRepository
{
IEnumerable<City> GetAll();
}
}
namespace AspCoreDIContainerSample.Repository
{
public class CityRepository : ICityRepository
{
public IEnumerable<City> GetAll()
{
// send back some hard-coded data
return new List<City>
{
new City { Id = "3938ca15-cba2-44a5-bd53-b5d6a5e306f5", Name = "Ankara" },
new City { Id = "cfd61cbf-a161-440f-9d1d-fdef32379b71", Name = "Samsun" }
};
}
}
}
Yukarıda oluşturduğumuz ICityRepository interface'i aşağıda tanımlayacağımız CityService sınıfına constructer-injected parameter olarak vereceğiz.
namespace AspCoreDIContainerSample.Service
{
public interface ICityService
{
List<City> GetAllCities();
}
}
namespace AspCoreDIContainerSample.Application
{
public class CityService : ICityService
{
private readonly ICityRepository cityRepository;
public CityService(ICityRepository cityRepository)
{
this.cityRepository = cityRepository;
}
public List<City> GetAllCities()
{
return this.cityRepository.GetAll().ToList();
}
}
}
Register All Used Components
Yukarıda kullandığımız repository ve service objelerini container'a implementasyonlarını belirterek register etmemiz gerekiyor. Bunu için aşağıdaki gibi proje solution'ında bulunan Startup.cs sınıfından faydalanacağız. Startup.cs proje ilk run edildiğinde çalışan kod satırlarının bulunduğu sınıftır. Bizde proje ilk run edildiğinde ilgili kullanılan bağımlılıkları register edeceğiz. Startup.cs de bulunan ConfigureServices adlı metot register işlemlerini yapmak için default gelen bir metottur. Aşağıdaki gibi tanımlamalarımızı yapalım.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ICityRepository, CityRepository>();
services.AddScoped<ICityService, CityService>();
}
Yukarıda görüldüğü üzre ICityRepository ve ICityService interface'lerini implementasyonlarını belirterek AddScoped metodunu kullanarak register ettik. Peki ne bu AddScoped ?
Dependency Injection Lifetimes - Scoped, Singleton, Transient
Dependency injection lifetime container'dan istenen objenin ne zaman instance create edileceğini yada ne zaman yeniden create edilmesi gerektiğini sağlar. Container'da lifetime tanımı yapabilmemizi sağlayan 3 state vardır. Sırasıyla bu üçlüye bakacak olursak;
Scoped : Her scope için tek bir instance yaratılmasını sağlayan lifetime adı dır. Örneğin web projesi için projenize gelen her bir HttpRequest için ilgili instance'ı yaratıp container'da tutar ve o http lifecycle'ı boyunca hep aynı instance'ı kullanır.
Singleton : İsminden de anlaşılacağı üzre singleton yani uygulama ilk çalıştığında tek bir instance yaratır ve sonrasından uygulama stop olana kadar bu instance'ı kullanır.
Transient : Bu lifetime ise ilgili obje her istendiğinde yeni bir instance yaratır ve özellikle stateless servisler için best-practice olarak kullanılır.
Biz yukarıda kullandığımız interface'lerin lifetime'larını Scoped olarak tanımladık ancak ihtiyaca göre Singleton yada Transient olarak da tanımlayabilirdik.
services.AddSingleton<ICityRepository, CityRepository>();
services.AddSingleton<ICityService, CityService>();
Using Registered Components
Son adım olarak register ettiğimiz bu service'leri kullanmak kaldı. Aşağıdaki gibi CityController içerisinde container'da register edilen instance'ları kullanarak geriye cityList return edelim.
namespace AspCoreDIContainerSample.Api
{
public class CityController : Controller
{
private readonly ICityService _cityService;
public CityController(ICityService cityService)
{
this._cityService= cityService;
}
[HttpGet]
public List<City> GetCityList()
{
return this.cityService.GetAllCities();
}
}
}
Asp.Net Core için Dependency Injection kullanım örneğimiz bu kadardı. Yukarıdaki end-point'e call yaparak geriye Samsun ve Ankara illerinin bulunduğu array'i return ettiğini göreceğiz.
Built-in dependency injection castle, ninject veya autofac kadar zenginliğe sahip bir IoC container değildir ancak oldukça basit kullanıma sahip hızlı ve implementasyonu kolay bir container dır. Eğer isterseniz bu container'ın asp.net core için bir kütüphanesi geliştirildiyse default built-in container ile replace de edebilirsiniz. Sonraki Asp.Net Core DI yazımızda 3rd party asp.net core DI container'larından birini nasıl built-in container yerine kullanabiliriz inceleyeceğiz.