Использование Keyed Services для использования нескольких экземпляров одного типа; как ввести ключ IOptions?

С появлением сервисов с ключами в .NET 8 мы теперь можем запрашивать конкретные реализации интерфейса в конструкторе:

public class MotorController
{
    private readonly IMotor _motor;
    public MotorController([FromKeyedServices("AxisX")] IMotor motor)
    {
        _motor = motor;        
    }
}

Это классное дополнение к языку. Одним из полезных приложений может быть внедрение разных экземпляров одного и того же типа в разные классы на основе служебного ключа.

Примечание. Я понимаю, что, вероятно, нет веской причины делать это в приложениях ASP.NET, которые составляют подавляющее большинство проектов C#. Однако для более нишевых приложений, над которыми я работаю, это будет полезным способом реализации нескольких экземпляров реальных физических устройств.

Препятствием для использования этого является то, что в настоящее время не существует способа ключа и сопоставления экземпляров IOptions, которые внедряются в классы; см. этот пример (максимально упрощенный):

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

// Create a simple configuration (stand-in for JSON file, etc.)
var configBuilder = new ConfigurationBuilder();

configBuilder.AddInMemoryCollection(new Dictionary<string, string?>
{
    { "Motors:X:AxisNumber", "1"},
    { "Motors:Y:AxisNumber", "2"},
    { "Motors:Z:AxisNumber", "3"}
});
var config = configBuilder.Build();

// Create a service collection with keyed services for different instances of the same type
var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedSingleton<Motor>("X");
serviceCollection.AddKeyedSingleton<Motor>("Y");
serviceCollection.AddKeyedSingleton<Motor>("Z");

// QUESTION: How do we map different config sections to different keyed services?
serviceCollection.Configure<MotorOptions>(config.GetSection("Motors:X"));

var serviceProvider = serviceCollection.BuildServiceProvider();

var motorX = serviceProvider.GetRequiredKeyedService<Motor>("X");
var motorY = serviceProvider.GetRequiredKeyedService<Motor>("Y");
var motorZ = serviceProvider.GetRequiredKeyedService<Motor>("Z");

Console.WriteLine("Motor X Axis: " + motorX.AxisNumber);
Console.WriteLine("Motor Y Axis: " + motorY.AxisNumber);
Console.WriteLine("Motor Z Axis: " + motorZ.AxisNumber);

public class Motor
{
    public Motor(IOptions<MotorOptions> options)
    {
        AxisNumber = options.Value.AxisNumber;
    }

    public int AxisNumber { get; }
}

public class MotorOptions
{
    public int AxisNumber { get; set; }
}

Запустив этот пример, вы получите одинаковый номер оси для каждого экземпляра, поскольку все они используют один экземпляр IOptions<MotorOptions>:

Motor X Axis: 1
Motor Y Axis: 1
Motor Z Axis: 1

Это можно сделать, вручную создав и привязав каждый экземпляр класса параметров, а затем передав соответствующий экземпляр параметров конструктору для каждого экземпляра службы с ключом:

MotorOptions optionsX = new();
MotorOptions optionsY = new();
MotorOptions optionsZ = new();

config.Bind("Motors:X", optionsX);
config.Bind("Motors:Y", optionsY);
config.Bind("Motors:Z", optionsZ);

// Create a service collection with keyed services for different instances of the same type
serviceCollection.AddKeyedSingleton<Motor>("X", new Motor(Options.Create(optionsX)));
serviceCollection.AddKeyedSingleton<Motor>("Y", new Motor(Options.Create(optionsY)));
serviceCollection.AddKeyedSingleton<Motor>("Z", new Motor(Options.Create(optionsZ)));

Однако этот подход довольно неуклюж, особенно в менее тривиальном примере, где вместе с аргументом IOptions в конструктор вводятся многие другие типы.

Есть какие-нибудь идеи по поводу более изящного решения этой проблемы, или это слишком далеко за пределами предполагаемого использования служб с ключами?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Шаблон параметров имеет поддержку именованных параметров с помощью IConfigureNamedOptions , который можно комбинировать с ServiceKeyAttribute, чтобы получить ключ зарегистрированного экземпляра.

Образец кода:

// register named options
services.Configure<MyOpts>("X", ...);
services.Configure<MyOpts>("Y", ...);

services.AddKeyedTransient<MyService>("X");
services.AddKeyedTransient<MyService>("Y");

public class MyService
{
    public MyOpts MyOpts { get; }

    public MyService(IOptionsFactory<MyOpts> opts, [ServiceKey] string servKey)
    {
        MyOpts = opts.Create(servKey); // use the service key to get named options
    }
}

Не знал о «Именованных параметрах», но это именно то, что я искал, спасибо!

daedalus12 27.06.2024 18:31

Другие вопросы по теме