从应用程序代码调用“BuildServiceProvider”会导致复制单例警告。我如何避免这种情况?

pun*_*ter 13 c# dependency-injection asp.net-core

我刚刚从另一个项目的最后粘贴了 4 行,它可以工作,但我收到警告..我显然不太了解 DI ......它想让我改变什么?

  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);
    }
Run Code Online (Sandbox Code Playgroud)

Ram*_*yev 7

如果在 ConfigureServices 中调用 BuildServiceProvider(),则会显示警告“从应用程序代码调用‘BuildServiceProvider’会导致创建单例服务的附加副本”

我解决了这个问题:

创建另一个函数(传递的参数是 IServiceCollection)并进入函数调用 BuildServiceProvider()

在此处输入图片说明

例如你的代码应该是:

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;
}
Run Code Online (Sandbox Code Playgroud)

或者使用 IApplicationBuilder 的 ApplicationServices。ApplicationServices 的类型是 IServiceProvider。

我提到这个解决方案仅用于删除警告

调用 BuildServiceProvider 会创建第二个容器,该容器可以创建撕裂的单例并导致对跨多个容器的对象图的引用。


24.01.2021 更新

我读了亚当弗里曼的Pro ASP.NET Core 3 8th书。为此目的,Adam Freeman 使用app.ApplicationServices而不是services.BuildServiceProvider()在第 157 页中,该应用程序是此方法位于 Startup.cs 中的配置方法的参数

我认为正确的版本是使用应用程序的 ApplicationServices 属性,该应用程序是 Configure 方法参数中的 IApplicationBuilder 。ApplicationServices 的类型是 IServiceProvider。

在此处输入图片说明

Adam Freeman 的 Pro ASP.NET Core 3 第 8 本书:Pro ASP.NET Core 3

Adam Freeman 的示例项目:SportStore 项目的 Startup.csSportStore 项目的 SeedData.cs

Microsoft 对 DI 的建议:ASP.NET Core 中的依赖注入

类似的问题的答案#1:/sf/answers/3924094891//sf/answers/3939461921/

  • 从另一个函数调用此方法不是正确的解决方案。您应该避免从代码中的任何地方调用它。这只是删除警告。 (23认同)
  • 要删除警告,您只需抑制它即可。 (3认同)