Параметры автозапуска во время выполнения

Я новичок в autofac и ищу лучшие практики по передаче значений времени выполнения в конструктор. Я прочитал кучу вопросов о стековом потоке, где они задаются, но ни один из них полностью не раскрыт. Должны ли мы использовать делегатов, фабрику для создания сервиса и т. Д. Я знаю, что передача контейнера - не лучший способ сделать это.

В моем конкретном случае у меня есть служба, которая имеет доступ к нескольким зависимостям, например, ведение журнала, поставщик данных и т. Д. Наряду с несколькими передаваемыми службами у меня также есть параметры времени выполнения, которые мне нужно записать, скажем, идентификатор пользователя, пароль. Идентификатор пользователя и пароль требуются для SomeService и ищутся, когда веб-программа просмотра выполняет определенное действие. Ниже то, что я имею, и подчеркнуло проблему.

public class SomeService : ISomeService
{
    private readonly IDataProvider _dataProvider;
    private readonly ILog _log;
    private readonly string _username;  
    private readonly string _password;

    public SomeService(IDataProvider dataProvider, ILog log,
        string username, string password)
    {
      _dataProvider = dataProvider;
      _log = log;
      _username = username;
      _password = password;
    }
}

Поставщик данных и журнал настраиваются в режиме автозапуска

builder.RegisterType<DataProviderService>().As<IDataProvider>()
builder.RegisterType<SomeLogService>().As<ILog>()

Большая часть функциональности этого "SomeService" требует имени пользователя и пароля для проверки перед выполнением задач, поэтому решил, что лучше всего перейти в конструктор при создании, но никогда не сталкивался с требованиями времени выполнения для autofac. Я рассмотрел вопрос Autofac - разрешение параметров времени выполнения без необходимости передавать контейнер, и он кажется близким к тому, что мне нужно, но мне нужно больше отзывов о том, как это можно сделать.

2 ответа

Решение

В общем, вы должны предотвратить передачу значений времени выполнения в конструкторы. Это сильно усложнит ваш дизайн и вашу конфигурацию DI. Конструкторы предназначены для зависимостей и значений конфигурации. Передайте значения времени выполнения через аргументы метода или внедрите сервис, который позволяет вам получить эти значения времени выполнения. Возьмите например IUserContext сервис, позволяющий получить имя текущего вошедшего в систему пользователя.

AutoFac поддерживает разрешение служб с параметрами времени выполнения посредством концепции параметризованного создания экземпляров.

В конструкторе клиента с зависимостью от службы с конкретными параметрами времени выполнения объявите свою зависимость как Func который возвращает эту зависимость в терминах своих строго типизированных параметров.

Например Func<string, ISomeService> myService

Когда AutoFac видит Func он создает делегат, который действует как фабричный метод для создания сервиса.

Из документации:

Если тип T зарегистрирован в контейнере, Autofac автоматически разрешит зависимости от Func как фабрики, которые создают экземпляры T через контейнер.

Невозможно иметь дубликаты типов в списке параметров вашей зависимости, как в случае ISomeService в вашем вопросе. Например Func<string, string, ISomeService> не будет работать. В этом случае вам необходимо указать собственную фабрику делегатов.

Из документации:

Фабричные адаптеры предоставляют функции создания экземпляров контейнера для управляемых компонентов, не подвергая их воздействию самого контейнера.

Одним из способов реализации этого подхода является объявление типа делегата вместе с определением вашего типа, которое AutoFac будет использовать в качестве фабричного метода.

Например

public class SomeService : ISomeService
{
    // Factory method
    public delegate SomeService Factory(string userName, string password);

    public SomeService(IDataProvider dataProvider,
        ILog log,
        string username,
        string password)
    {
        // ..

Ваш клиент ISomeService будет выглядеть следующим образом:

public class MyClient
{
    public MyClient(SomeService.Factory serviceFactory)
    {
        // Resolve ISomeService using factory method
        // passing runtime parameters
        _myService = serviceFactory("this", "that");
    }
}

Обратите внимание, что все параметры, не связанные со временем выполнения, для службы (в вашем случае IDataProvider и ILog) продолжают автоматически разрешаться контейнером.

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