Как создать и настроить несколько экземпляров одного и того же класса с внедрением зависимостей?

Как можно создать и настроить несколько экземпляров одного и того же класса с внедрением зависимостей?

Я видел похожие вопросы/учебники, но пока не понимаю.

Вот пример:

Контейнер

public class App()
{
    public IHost Host { get; }

    public App()
    {
        Host = Microsoft.Extensions.Hosting.Host.
            CreateDefaultBuilder().
            ConfigureServices((context, services) =>
            {
                services.AddSingleton<ITemperatureSensor, AcmeTemperatureSensor>();
                services.AddSingleton<IAcmeMachine, AcmeMachine>();
                services.Configure<TemperatureSensorOptions>(context.Configuration.GetSection(nameof(TemperatureSensorOptions)));
            }).
            Build();
    }
}

Акме машина

Именно сюда должны быть вставлены датчики температуры.

public class AcmeMachine()
{
    public AcmeMachine( Something? )
    {
        // How inject the temperature sensors?
    }
    
    ITemperatureSensor WaterSensor
    ITemperatureSensor AirSensor
}

Датчики температуры

public interface ITemperatureSensor
{
    string SerialNumber;
    double GetTemperature();
}

public class AcmeTemperatureSensor()
{
    public string SerialNumber { get; }
    
    public AcmeTemperatureSensor(IOptions<TemperatureSensorOptions> options)
    {
        SerialNumber = options.Value.SerialNumber;
    }
    
    public double GetTemperature()
    {
        return 25.0;
    }
}

Настройки

appsettings.json
{
    "WaterSensor": {
        "TemperatureSensorOptions": {
            "SerialNumber": "123",
            },
    },
    "AirSensor": {
        "TemperatureSensorOptions": {
            "SerialNumber": "456",
            },
    }
}

Это тоже очень похожий вопрос

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

Ответы 2

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

Я бы предложил изменить настройки приложения на что-то вроде:

{
  // ...
  "Sensors": {
    "WaterSensor": {
      "TemperatureSensorOptions": {
        "SerialNumber": "123"
      }
    },
    "AirSensor": {
      "TemperatureSensorOptions": {
        "SerialNumber": "456"
      }
    }
  }
}

Тогда вы сможете прочитать конфиг как:

builder.Services.Configure<Dictionary<string, Sensor>>(builder.Configuration.GetSection("Sensors"));

class Sensor
{
    public TemperatureSensorOptions TemperatureSensorOptions { get; set; }
}

class TemperatureSensorOptions
{
    public string SerialNumber { get; set; }
}

И внедрить его в какой-нибудь сервис как IOptions<Dictionary<string, Sensor>>.

Или используйте специальный класс вместо Dictionary:

builder.Services.Configure<Sensors>(builder.Configuration.GetSection("Sensors"));

class Sensors
{
    public Sensor WaterSensor { get; set; }
    public Sensor AirSensor { get; set; }
}

Обратите внимание, что если датчик не имеет других настроек, TemperatureSensorOptions можно сгладить/удалить, чтобы упростить настройку.

Если вам нужно построить AcmeTemperatureSensor, внедрив туда эти настройки, вам нужно будет либо создать их вручную, либо следовать заводскому шаблону.

Из комментариев:

Если кто-то использует ручное построение или заводской шаблон, есть ли хороший способ получить доступ к службам контейнеров IoC?

Лично я бы выбрал фабричный шаблон, который можно довольно легко реализовать с помощью Func<> (хотя у него могут быть и некоторые недостатки, например, добавление заводских параметров станет не таким простым):

enpointServices.AddTransient<Func<TemperatureSensorOptions, AcmeTemperatureSensor>>(sp => 
    opts => new AcmeTemperatureSensor(opts, sp.GetRequiredService<ILogger<AcmeTemperatureSensor>>()));

А затем введите Func<TemperatureSensorOptions, AcmeTemperatureSensor> как зависимость и вызовите ее.

Также обычно в таких случаях я создаю и регистрирую класс DI, например AcmeTemperatureSensorDeps, чтобы инкапсулировать все AcmeTemperatureSensor зависимости от DI, чтобы упростить управление ими.

Если кто-то использует ручное построение или заводской шаблон, есть ли хороший способ получить доступ к службам контейнеров IoC? Посоветуете что-нибудь подобное _logger = Host.Services.GetRequiredService<ILogger<Foo>>();?

JasonC 14.04.2023 22:49

@JasonC Немного обновил ответ, посмотрите.

Guru Stron 15.04.2023 11:08

Спасибо за обновление вашего ответа. Вы упомянули добавление класса, который инкапсулирует все необходимые зависимости, что упрощает управление ими. Что вы имеете в виду под проще управлять? Просто класс, который его использует, имеет только один аргумент в конструкторе?

JasonC 17.04.2023 22:18

@JasonC «Просто класс, который его использует, имеет только один аргумент в конструкторе?» - короче - да, у вашего класса есть один параметр кроме "фабричных".

Guru Stron 17.04.2023 22:57

Вы можете изменить конструкторы классов на следующие:

public AcmeMachine(ITemperatureSensor waterSensor, ITemperatureSensor airSensor)
public AcmeTemperatureSensor(TemperatureSensorOptions options)

С этими подписями вы можете иметь следующую конфигурацию DI:

var temp = Configuration.GetSection("WaterSensor").Get<TemperatureSensorOptions>();
var air = Configuration.GetSection("AirSensor").Get<TemperatureSensorOptions>();

services.AddSingleton(sp => new AcmeMachine(
    waterSensor: new AcmeTemperatureSensor(temp),
    airSensor: new AcmeTemperatureSensor(air));

Было бы это хорошим решением, если бы было N датчиков? Скажем, N = 5, 10 или 100?

JasonC 14.04.2023 22:43

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

Steven 15.04.2023 10:45

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

Похожие вопросы

Azure DevOps 2022 не будет создавать однофайловые/автономные исполняемые файлы
Как отложить выполнение запроса EF до тех пор, пока он действительно не понадобится
Клиентская проекция содержит ссылку на постоянное выражение сквозного метода экземпляра
Получение данных из последовательного порта на более высоких скоростях передачи с использованием С#
Поле Fluent Validation Boolean должно быть обязательным
Как вы обновляете свойство LastUpdate родительских сущностей при добавлении или изменении дочернего элемента на любом уровне иерархии под ним в Entity Framework?
Невозможно заменить выбранный номер регулярного выражения типом следующего номера
С# перезаписывает текущую строку в консоли, не работая в conhost.exe
Является ли моя устойчивая функция Azure детерминированной?
Приложение Maui работает нормально на компьютере с Windows, но получает ошибки, когда я пытаюсь использовать физическое устройство Android