IConfiguration DI вопрос о курице и яйце

У меня есть приложение ASP.NET Core 7 MVC, написанное на С#. Я хочу создать собственный IConfiguration, чтобы добавить его к уже зарегистрированным провайдерам.

Я хотел бы использовать некоторые сервисы, зарегистрированные в коллекции сервисов DI, для реализации некоторых поставщиков конфигурации. Эти услуги обычно получают значения от поставщика IConfiguration.

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

Например, я мог бы добавить IConfiguration на основе репозитория, которому требуется строка подключения к репозиторию, и я мог бы сохранить настройки для других служб в репозитории. Репозиторию нужна служба доступа, например EF. но с другими сервисными зависимостями. Без DI я мог бы написать:

int attempts = Confguration["attempts"];
string connectitonString = Configuration["repository"]
IRetry retry = new Retry(attempts);
IConnect connect = new Connect(connectionString, retry);
IRead read = new Read();
IStore store = new Store(connect, read); // has secret
IConfiguration moreConfiguration = new MoreConfiguration(store);
builder.Confguration.Add(moreConfiguration);
string secret = Configuration["secret"];
IService finally = new Service(secret);
finally.DoSomething(...);

Наивно я мог бы попытаться создать экземпляр «временного» поставщика услуг, создать поставщика конфигурации, используя службы из временного, добавить его в зарегистрированные конфигурации, а затем добавить дополнительные службы. Мне это не кажется хорошей идеей, особенно если у меня есть синглтоны и сервисы с ограниченной областью действия.

int attempts = Confguration["attempts"];
string connectitonString = Configuration["repository"]
builder.Services
    .AddSingleton<IRetry>(_ => new Retry(attempts))
    .AddSingleton<IConnect>(p =>
        {
               IRetry r = p.GetRequiredService<IRetry>();
               return new Connect(connectionString, retry);
        })
    .AddSingleton<IRead, Read>();
    .AddSingleton<IStore>()(p =>
        {
            IConnect c = p.GetRequiredService<IConnect>();
            IRead r = p.GetRequiredService<IRead>();
            return new Store(c, r);
        });
ServiceProvider intermediate = builder.Services.BuildServiceProvider();
IStore store = intermediate.GetRequiredService<IStore>();
IConfiguration moreConfiguration = new MoreConfiguration(store);
builder.Confguration.Add(moreConfiguration);
string secret = Configuration["secret"];
builder.Services.AddTransient<IService>(_ => new Service(secret));
ServiceProvider final = builder.Services.BuildServiceProvider();
IService finally = final.GetRequiredService<IService>();
finally.DoSomething(...);

Есть ли способ действительно использовать поставщика услуг, затем добавить больше услуг, а затем снова использовать его? Или я сумасшедший?

Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
93
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Вы не можете получить доступ к IServiceProvider без создания временного экземпляра, потому что действия, добавленные с помощью ConfigureAppConfiguration, выполняются до создания корневого поставщика услуг. Но вы можете получить доступ к информации, «добавленной в конфигурацию предыдущими вызовами» в ConfigureAppConfiguration, просто создав IConfigurationRoot из configurationBuilder.

см. пример ниже (также следуйте docs — пример взят оттуда):

using CustomProvider.Example.Providers;

namespace Microsoft.Extensions.Configuration;

public static class ConfigurationBuilderExtensions
{
    public static IConfigurationBuilder AddEntityConfiguration(
        this IConfigurationBuilder builder)
    {
        var tempConfig = builder.Build();
        var connectionString =
            tempConfig.GetConnectionString("WidgetConnectionString");

        return builder.Add(new EntityConfigurationSource(connectionString));
    }
}

Редактировать

Поскольку в вопросе была предоставлена ​​​​дополнительная информация, я попытаюсь дать вам конкретный пример того, как вы можете достичь того, чего хотите, без создания временного ServiceProvider... (приведенный пример имеет ошибку компиляции, поэтому я изменил его для работы).

Во-первых, вам нужно создать ConfigurationProvider

public class MoreConfigurationProvider : ConfigurationProvider
{
    private readonly int _attempts;
    private readonly string _connectionString;

    public MoreConfigurationProvider(int attempts, string connectionString)
    {
        _attempts = attempts;
        _connectionString = connectionString;
    }
    
    public override void Load()
    {
        IRetry retry = new Retry(_attempts);
        IConnect connect = new Connect(_connectionString, retry);
        IRead read = new Read();
        IStore store = new Store(connect, read);
        
        // Simple version as per example
        Data.Add("secret", store.GetSecret());
    }
}

Тогда создайте IConfigurationSource

public class MoreConfigurationSource : IConfigurationSource
{
    public int Attempts { get; set; }
    public string ConnectionString { get; set; }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new MoreConfigurationProvider(Attempts, ConnectionString);
    }
}

Измените свой класс Service, чтобы принять IConfiguration (это необязательно)

public class Service : IService
{
    public Service(IConfiguration configuration)
    {
        string sercert = configuration["secret"];
    }
}

Наконец, измените свою программу.cs

int attempts = int.Parse(builder.Configuration["attempts"]);
string connectionString = builder.Configuration["repository"];

builder.Services
    .AddSingleton<IRetry>(_ => new Retry(attempts))
    .AddSingleton<IConnect>(p =>
    {
        IRetry r = p.GetRequiredService<IRetry>();
        return new Connect(connectionString, r);
    })
    .AddSingleton<IRead, Read>()
    .AddSingleton<IStore>(p => 
    {
        IConnect c = p.GetRequiredService<IConnect>();
        IRead r = p.GetRequiredService<IRead>();
        return new Store(c, r); 
    });

builder.Configuration.Add<MoreConfigurationSource>(mc =>
{
    mc.ConnectionString = connectionString;
    mc.Attempts = attempts;
});

builder.Services.AddTransient<IService>();

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

Skip Saillors 04.05.2023 17:47

Обновил вопрос на основе приведенного вами примера псевдокода :)

Florent Bunjaku 04.05.2023 21:43

Класс MoreConfiguration теперь тесно связан с Retry, Read, Connect и Store. Я думаю, что это то, где я задаюсь вопросом. Есть ли способ получить c'tor-инъекцию этих сервисов? MoreConfiguration(IRetry r, IConnect c, IRead e, IStore s) {...}

Skip Saillors 04.05.2023 22:11

Поскольку поставщики конфигурации добавляются до сборки Host, вам придется использовать его, как я описал, или придерживаться вашего подхода, создающего sp вручную.

Florent Bunjaku 04.05.2023 23:08

Понятно, спасибо. Это говорит о том, что поставщики конфигурации .NET должны быть разработаны без преимуществ .NET DI.

Skip Saillors 06.05.2023 17:53

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