Как правильно разрешить службы для использования в ConfigureServices () в ASP.NET Core 3.1?

У меня есть приложение на основе ASP.NET Core 3.1. Во время запуска приложения в ConfigureServices(IServiceCollection services) я хочу зарегистрировать свои службы. Но во время настройки служб я хочу загрузить приложение на основе настроек, найденных в базе данных.

Вот мой код

public void ConfigureServices(IServiceCollection services)
{
    // Register context
    services.AddDbContext<AppDbContext>(options =>
    {
        options.UseMySql(Configuration.GetConnectionString("MySqlServerConnection"));
    });

    // Reister the setting provider
    services.AddSingleton<IAppSetting, AppSettings>();

    // Create a resolver which is WRONG!!
    var resolver = services.BuildServiceProvider();

    var setting = resolver.GetService<IAppSetting>();

    List<string> allowedCors = new List<string>()
    {
         setting.MainUrl.ToString()
    };

    if (setting.HasCustomAssetsUri)
    {
        allowedCors.Add(settings.AssetsUrl.ToString());
    }

    if (settings.HasCustomPhotosUri)
    {
        allowedCors.Add(settings.PhotosUrl.ToString());
    }

    services.AddCors(options =>
    {
        options.AddPolicy("AllowSubDomainTraffic",
        builder =>
        {
            builder.WithOrigins(allowedCors.ToArray())
                   .AllowAnyHeader()
                   .AllowAnyMethod();
        });
    });

    // More services
}

Как вы можете видеть в приведенном выше коде, я регистрирую IAppSetting, но сразу хочу использовать его для доступа к базе данных, поэтому получите текущую конфигурацию. В настоящее время я вызываю services.BuildServiceProvider(), чтобы создать новый преобразователь, в котором я могу затем использовать его для создания экземпляра IAppSetting.

Моя реализация AppSetting зависит от AppDbContext, который также зарегистрирован. Вот мой AppSetting

public class AppSetting : IAppSetting
{
    private AppDbContext context;

    public AppSetting(AppDbContext context)
    {
        this.context = context;
    }

    // methods...
}

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

Как я могу правильно зарегистрировать и затем разрешить IAppSetting в методе ConfigureServices(), чтобы я мог использовать его для доступа к настройкам, найденным в базе данных?

Обновлено

Я попытался использовать решение, представленное Nkosi, но получил следующую ошибку

System.InvalidOperationException: «Не удается разрешить службу с областью действия« IAppSetting »от корневого поставщика».

Вот полная трассировка стека.

This exception was originally thrown at this call stack:
    Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(System.Type, Microsoft.Extensions.DependencyInjection.IServiceScope, Microsoft.Extensions.DependencyInjection.IServiceScope)
    Microsoft.Extensions.DependencyInjection.ServiceProvider.Microsoft.Extensions.DependencyInjection.ServiceLookup.IServiceProviderEngineCallback.OnResolve(System.Type, Microsoft.Extensions.DependencyInjection.IServiceScope)
    Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(System.Type, Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope)
    Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(System.Type)
    Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(System.IServiceProvider, System.Type)
    Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(Microsoft.Extensions.DependencyInjection.ServiceLookup.FactoryCallSite, Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext)
    Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceCallSite, Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext)
    ...
    [Call Stack Truncated]

person Community    schedule 05.02.2020    source источник
comment
Вам не нужно AppSetting, чтобы начать остроумие. Вместо этого используйте систему конфигурации для загрузки параметров из базы данных.   -  person Panagiotis Kanavos    schedule 06.02.2020
comment
Пользовательская конфигурация Пример провайдера в документации загружает настройки из базы данных с помощью EF Core. Вы можете легко использовать это для загрузки своих настроек.   -  person Panagiotis Kanavos    schedule 06.02.2020
comment
Что касается доступа к конфигурации на этапе настройки DI, это уже поддерживается. Вы можете получить нужные значения конфигурации из объекта IConfiguration, переданного в Startup.   -  person Panagiotis Kanavos    schedule 06.02.2020


Ответы (1)


Ссылка Используйте службы DI для настройки параметров

Настройте параметры CORS с помощью DI, переместив всю логику в делегат настройки.

//...

// Register context
services.AddDbContext<AppDbContext>(options => {
    options.UseMySql(Configuration.GetConnectionString("MySqlServerConnection"));
});

// Reister the setting provider
services.AddScoped<IAppSetting, AppSettings>(); 

//configure CORS options using DI
services.AddOptions<CorsOptions>()
    .Configure<IServiceScopeFactory>((options, sp) => {
        using(var scope = sp.CreateScope()) {
            IAppSetting settings = scope.ServiceProvider.GetRequiredService<IAppSetting>();
            List<string> allowedCors = new List<string>() {
                 setting.MainUrl.ToString()
            };

            if (setting.HasCustomAssetsUri) {
                allowedCors.Add(settings.AssetsUrl.ToString());
            }

            if (settings.HasCustomPhotosUri) {
                allowedCors.Add(settings.PhotosUrl.ToString());
            }
            options.AddPolicy("AllowSubDomainTraffic", builder => {
                builder.WithOrigins(allowedCors.ToArray())
                       .AllowAnyHeader()
                       .AllowAnyMethod();
            });
        }
    });

services.AddCors();

//...

таким образом теперь все откладывается до того момента, когда они действительно необходимы, и вы избегаете преждевременного создания коллекции сервисов.

person Nkosi    schedule 06.02.2020
comment
Спасибо за этот ответ. Я получаю следующую ошибку System.InvalidOperationException: 'Cannot resolve scoped service 'IAppSetting' from root provider.' Я обновлю свой вопрос, добавив полную трассировку стека - person ; 07.02.2020
comment
@John проблема в том, что DbContext зарегистрирован как область действия, а настройки - одноэлементные. синглтоны не могут иметь зависимостей в области видимости. Проверьте предупреждение здесь docs.microsoft .com / en-us / aspnet / core / Основы / - person Nkosi; 07.02.2020
comment
Спасибо. Я все еще получаю ту же ошибку даже после изменения services.AddSingleton<IAppSetting, AppSettings>(); на services.AddScoped<IAppSetting, AppSettings>(); - person ; 07.02.2020
comment
@John обновлен с использованием локальной области видимости. Обратите внимание на изменения - person Nkosi; 07.02.2020
comment
Это сработало. Но разве CreateScope() не создает второй экземпляр ServiceProvider, который вызывает службу настроек второго экземпляра? - person ; 07.02.2020
comment
@John только для настройки (при первом вызове), чтобы заполнить параметры. - person Nkosi; 07.02.2020
comment
Тогда это ничем не отличается от звонка var resolver = services.BuildServiceProvider();, верно? - person ; 07.02.2020
comment
Нет. Это происходит хорошо после того, как первоначальный провайдер был построен (отложен), поэтому он отличается. - person Nkosi; 07.02.2020
comment
Мне нужен экземпляр IAppSetting для создания других служб, таких как ISettingService, и добавления провайдеров аутентификации. Могу ли я просто создать новую область для каждого параметра, который мне нужно настроить, или есть способ создать область один раз и использовать ее для настройки нескольких параметров? - person ; 07.02.2020
comment
@John выглядит так, основываясь на настройках, зависящих от контекста dbcontext с заданной областью. Это может быть крайний случай - person Nkosi; 07.02.2020