Использование Сервисов по Ключу в .NET 8
В DI-контейнер .NET 8 наконец-то добавили то, что уже много лет включено в другие контейнеры — использование сервиса по ключу, позволяющее нам внедрять несколько сервисов одного типа, но с разной реализацией. Ключ позволяет определить, какую реализацию использовать.
Допустим, у нас есть интерфейс
public interface IVehicle
{
int Wheels { get; }
}
И две реализации:
public class Car : IVehicle
{
public int NoOfWheels => 4;
}
public class Bike : IVehicle
{
public int NoOfWheels => 2;
}
До .NET 8 можно было добавлять несколько реализаций:
builder.Services.AddScoped<IVehicle, Car>();
builder.Services.AddScoped<IVehicle, Bike>();
Однако, при внедрении использовалась последняя зарегистрированная реализация. Если бы мы хотели получить другие реализации, приходилось внедрять IEnumerable<IVehicle> и выбирать правильную реализацию уже в клиенте. Подробнее тут.
В .NET 8 можно разделить несколько сервисов с помощью ключа. Для IServiceCollection были добавлены методы расширения AddKeyedSingleton, AddKeyedScoped и AddKeyedTransient. Например:
builder.Services.AddKeyedScoped<IVehicle, Car>("car");
builder.Services.AddKeyedScoped<IVehicle, Bike>("bike");Теперь в минимальных API можно использовать атрибут FromKeyedServices:
app.MapGet("/api/carweels", (
[FromKeyedServices("car")] IVehicle v) =>
{
return Ok(new {
CarWheels = v.NoOfWheels
});
});Ключ определяет, какую реализацию необходимо использовать.
Аналогично для контроллеров:
[ApiController]
public class VehicleApiController : Controller
{
[HttpGet("carweels")]
public IActionResult CarWheels(
[FromKeyedServices("car")] IVehicle v)
{
return Ok(new {
CarWheels = v.NoOfWheels,
});
}
}
Интерфейс IServiceProvider также получил методы GetRequiredKeyedService (и GetKeyedService, чтобы избежать исключения при null).
var car = _serviceProvider
.GetRequiredKeyedService<IVehicle>("car");
Источник: https://www.roundthecode.com/dotnet-tutorials/keyed-services-dotnet-8-dependency-injection-update