У меня есть приложение 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(...);
Есть ли способ действительно использовать поставщика услуг, затем добавить больше услуг, а затем снова использовать его? Или я сумасшедший?





Вы не можете получить доступ к 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>();
Обновил вопрос на основе приведенного вами примера псевдокода :)
Класс MoreConfiguration теперь тесно связан с Retry, Read, Connect и Store. Я думаю, что это то, где я задаюсь вопросом. Есть ли способ получить c'tor-инъекцию этих сервисов? MoreConfiguration(IRetry r, IConnect c, IRead e, IStore s) {...}
Поскольку поставщики конфигурации добавляются до сборки Host, вам придется использовать его, как я описал, или придерживаться вашего подхода, создающего sp вручную.
Понятно, спасибо. Это говорит о том, что поставщики конфигурации .NET должны быть разработаны без преимуществ .NET DI.
Я добавил немного псевдокода и немного усложнил вариант использования, чтобы лучше описать мою затруднительную ситуацию. Я хочу зарегистрировать некоторые службы, затем использовать эти службы в своей конфигурации, а затем создать дополнительные службы с использованием новой конфигурации.