.NET dostarcza wbudowane klasy do zarządzania zależnościami, ServiceCollection oraz ServiceProvider.
Klasa ServiceCollection dostarcza metody (głównie extension methods) do rejestracji typów, interesują nas trzy z nich:
builder.Services.AddTransient()
builder.Services.AddScoped()
builder.Services.AddSingleton()
Transient – instancja jest tworzona za każdym razem kiedy jest requestowana.
Scoped – instancja jest tworzona w danym zakresie. W naszym przykładzie używamy Web API, gdzie scope jest realizowany w ramach przetwarzania żądania HTTP.
Singleton – instancja jest tworzona na czas działania aplikacji
Przygotowałem proste klasy testowe, abyśmy mogli zwizualizować, co to oznacza w praktyce. Celowo jest jedna klasa, która implementuje interfejs, oraz klasa zwykła, aby pokazać, że w kontenerze można zarejestrować zarówno jedną, jak i drugą.
public interface IGuidProvider
{
Guid Id { get; }
}
public class GuidProvider : IGuidProvider
{
public Guid Id { get; } = Guid.NewGuid();
}
public class GuidProviderProxy(IGuidProvider valueGenerator) // primary constructor
{
public Guid Id => valueGenerator.Id;
}
builder.Services.AddTransient<IGuidProvider, GuidProvider>();
builder.Services.AddTransient<GuidProviderProxy>();
builder.Services.AddScoped<IGuidProvider, GuidProvider>();
builder.Services.AddScoped<GuidProviderProxy>();
builder.Services.AddSingleton<IGuidProvider, GuidProvider>();
builder.Services.AddSingleton<GuidProviderProxy>();
Potrzebujemy jeszcze testowego kontrolera. W konstruktorze podajemy zależności, które chcemy wstrzyknąć, a Web API zajmuje się tworzeniem odpowiednich instancji z ServiceProvidera. Możemy wstrzykiwać zarówno interfejsy, jak i klasy!
[ApiController]
[Route("[controller]")]
public class ValueController : Controller
{
private readonly IGuidProvider _valueGenerator;
private readonly GuidProviderProxy _proxy;
public ValueController(
IGuidProvider valueGenerator,
GuidProviderProxy proxy)
{
_valueGenerator = valueGenerator;
_proxy = proxy;
}
[HttpGet]
public IActionResult GetValue()
{
return Ok(new
{
value1 = _valueGenerator.Id,
value2 = _proxy.Id
});
}
}
W Swaggerze albo w przeglądarce łatwo jesteśmy w stanie przetestować wyniki:
Transient – po każdym odświeżeniu dostajemy różnie wartości
Scoped – dostajemy te same wartości dla każdego requesta HTTP
Singleton – Ta sama wartość na czas działania aplikacji (poniżej też jest załączony gif, niestety nie widać odświeżania)
Zwracajcie uwagę na sposób rejestrowania typów — czasami już na wczesnym etapie można uniknąć wielu problemów!