Chamar 'BuildServiceProvider' do código do aplicativo resulta em cópia do aviso Singleton. Como evito isso?

8

Acabei de colar as 4 linhas no final de outro projeto e funciona, mas recebo um aviso .. Claramente não entendo o DI o suficiente ... O que ele quer que eu mude?

  public void ConfigureServices(IServiceCollection services)
        {
            if (HostingEnvironment.EnvironmentName == "Local")
            {
                services.AddHealthChecksUI()
               .AddHealthChecks()
               .AddCheck<TestWebApiControllerHealthCheck>("HomePageHealthCheck")
               .AddCheck<DatabaseHealthCheck>("DatabaseHealthCheck");
            }

        services.Configure<PwdrsSettings>(Configuration.GetSection("MySettings"));
        services.AddDbContext<PwdrsContext>(o => o.UseSqlServer(Configuration.GetConnectionString("PwdrsConnectionRoot")));

        services.AddMvc(o =>
        {
            o.Filters.Add<CustomExceptionFilter>();
        });

        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy", b => b
                .SetIsOriginAllowed((host) => true)
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());
        });

        services.AddSwaggerDocument();
        services.AddHttpContextAccessor();

        services.AddAutoMapper(typeof(ObjectMapperProfile));
        services.AddTransient<IEmailSender, EmailSender>();
        services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
        services.AddScoped(typeof(IAsyncRepository<>), typeof(Repository<>));
        services.AddScoped<IRfReportTypeRepository, RfReportTypeRepository>();
        services.AddScoped<IRfReportRepository, RfReportRepository>();
        services.AddScoped<IRfReportLookupsService, RfReportLookupsService>();
        services.AddScoped<IRfReportService, RfReportService>();

        services.Configure<RAFLogging>(Configuration.GetSection("RAFLogging"));
        ServiceProvider serviceProvider = services.BuildServiceProvider(); //WARNING IS HERE
        IOptions<RAFLogging> RAFLogger = serviceProvider.GetRequiredService<IOptions<RAFLogging>>();
        RegisterSerilogLogger logger = new RegisterSerilogLogger(RAFLogger);
    }
punkouter
fonte
2
Primeiro, por que você está construindo provedor? Isso pode ser um problema XY . Você pode reformatar a pergunta para obter uma imagem mais clara do problema atual e o que você está realmente tentando fazer?
Nkosi
Não tenho certeza. Acho que já tenho um e talvez isso esteja criando outro?
pUnkOuter
Como assim WARNING IS HERE? Forneça detalhes sobre o aviso. Mostre-nos o texto do aviso. Este é um aviso do compilador? Um aviso de algum plug-in de análise de código? Se sim, qual? Isso é uma exceção de tempo de execução? Mostre-nos todos os detalhes relevantes da exceção (mensagem, tipo, rastreamento de pilha, exceções internas).
24419 Steven
@punkouter "O que ele quer que eu mude": não crie o provedor de serviços manualmente, invocando BuildServiceProvider(). Este método deve ser chamado pelo Host apenas uma vez. O provedor de serviços duplicado pode levar a alguns erros inesperados.
itminus 25/11/19
O aviso é o título. Eu acho que IServiceCollection é onde eu deveria colocar esse logger de alguma forma? Preciso entender melhor IServiceCollection versus um ServiceProvider.
pUnkOuter

Respostas:

3

Se chamado BuildServiceProvider () em ConfigureServices, o aviso mostrado "Chamando 'BuildServiceProvider' do código do aplicativo resulta em uma cópia adicional dos serviços Singleton sendo criados"

eu resolvi esse problema:

Crie outra função (que passou o argumento IServiceCollection) e chame a função BuildServiceProvider ()

insira a descrição da imagem aqui

Por exemplo, seu código deve ser:

public void ConfigureServices(IServiceCollection services)
    {
        if (HostingEnvironment.EnvironmentName == "Local")
        {
            services.AddHealthChecksUI()
           .AddHealthChecks()
           .AddCheck<TestWebApiControllerHealthCheck>("HomePageHealthCheck")
           .AddCheck<DatabaseHealthCheck>("DatabaseHealthCheck");
        }

    services.Configure<PwdrsSettings>(Configuration.GetSection("MySettings"));
    services.AddDbContext<PwdrsContext>(o => o.UseSqlServer(Configuration.GetConnectionString("PwdrsConnectionRoot")));

    services.AddMvc(o =>
    {
        o.Filters.Add<CustomExceptionFilter>();
    });

    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy", b => b
            .SetIsOriginAllowed((host) => true)
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowCredentials());
    });

    services.AddSwaggerDocument();
    services.AddHttpContextAccessor();

    services.AddAutoMapper(typeof(ObjectMapperProfile));
    services.AddTransient<IEmailSender, EmailSender>();
    services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
    services.AddScoped(typeof(IAsyncRepository<>), typeof(Repository<>));
    services.AddScoped<IRfReportTypeRepository, RfReportTypeRepository>();
    services.AddScoped<IRfReportRepository, RfReportRepository>();
    services.AddScoped<IRfReportLookupsService, RfReportLookupsService>();
    services.AddScoped<IRfReportService, RfReportService>();

    RegisterSerilogLogger logger = CreateRegisterSerilogLogger(services);
}

private RegisterSerilogLogger CreateRegisterSerilogLogger(IServiceCollection services){
        services.Configure<RAFLogging>(Configuration.GetSection("RAFLogging"));
        ServiceProvider serviceProvider = services.BuildServiceProvider(); //No warning here ))
        IOptions<RAFLogging> RAFLogger = serviceProvider.GetRequiredService<IOptions<RAFLogging>>();
        RegisterSerilogLogger logger = new RegisterSerilogLogger(RAFLogger);
    return logger;
}

Ou use ApplicationServices of IApplicationBuilder. O tipo de ApplicationSerivces é IServiceProvider.

ATUALIZADO :

Eu mencionei que esta solução é remover aviso .

Eu acho que a versão correta é usar a propriedade ApplicationServices do aplicativo, que é IApplicationBuilder no parâmetro do método Configure. O tipo de ApplicationServices é IServiceProvider.

insira a descrição da imagem aqui

Ramil Aliyev
fonte
Vou tentar no meu tempo livre
Nijat Aliyev
1
Ótima solução! Eu estava preocupado com este aviso (Y)
Samra
7
Chamar esse método de outra função não é a solução certa. Você deve evitar chamá-lo de QUALQUER LUGAR no seu código. Isso está apenas removendo o aviso.
Adys
@ Adys Eu concordo com você, meu amigo, mencionei que esta solução é remover o aviso :) Acho que a versão correta é chamar ServiceProvider do aplicativo, qual aplicativo é IApplicationBuilder no método Configure
Ramil Aliyev
-1

O único objetivo de chamar 'BuildServiceProvider' é obter uma instância do provedor de serviços,

Para remover esta chamada e ainda poder usar o IServiceProvider, altere o método Configure para obtê-lo como parâmetro:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider provider)
Adys
fonte
Você não precisa injetar IServiceProvider porque app.ApplicationServices é IServiceProvider. - Ramil Aliyev 24 de fevereiro às 11:48
Ramil Aliyev