Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling 'BuildServiceProvider' from application code results in copy of Singleton warning. How do I avoid this?

I just pasted the 4 lines at the end from another project and it works but I get a warning.. I clearly do not understand DI well enough ... What does it want me to change ?

  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);
    }
like image 861
punkouter Avatar asked Nov 22 '19 17:11

punkouter


People also ask

Can I call BuildServiceProvider multiple times?

Calling BuildServiceProvider multiple times can cause serious trouble, because each call to BuildServiceProvider results in a new container instance with its own cache. This means that registrations that are expected to have the Singleton lifestyle, suddenly are created more than once.

What BuildServiceProvider does?

BuildServiceProvider(IServiceCollection, ServiceProviderOptions) Creates a ServiceProvider containing services from the provided IServiceCollection optionally enabling service-creation and scope validation.

What is IServiceCollection in .NET core?

} IServiceCollection is the collection of the service descriptors. We can register our services in this collection with different lifestyles (Transient, scoped, singleton) IServiceProvider is the simple built-in container that is included in ASP.NET Core that supports constructor injection by default.


1 Answers

If called BuildServiceProvider() in ConfigureServices, shown warning "Calling 'BuildServiceProvider' from application code results in a additional copy of Singleton services being created"

I solved this issue:

Create another function (which passed argument is IServiceCollection) and into the function call BuildServiceProvider()

enter image description here

For example your code it should be:

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;
}

Or use ApplicationServices of IApplicationBuilder. ApplicationSerivces's type is IServiceProvider.

I mention this solution is only for remove warning.

Calling BuildServiceProvider creates a second container, which can create torn singletons and cause references to object graphs across multiple containers.


UPDATED 24.01.2021

I read Adam Freeman's Pro ASP.NET Core 3 8th book. Adam Freeman used app.ApplicationServices instead of services.BuildServiceProvider() in page 157 for this purpose, that app is Configure method's parameter that this method located in Startup.cs

I thinks correct version is to use ApplicationServices property of app, which app is IApplicationBuilder in Configure method's parameter. ApplicationServices's type is IServiceProvider.

enter image description here

Adam Freeman's Pro ASP.NET Core 3 8th book : Pro ASP.NET Core 3

Adam Freeman's example project: SportStore project's Startup.cs, SportStore project's SeedData.cs

Microsoft's recommendations about DI : Dependency injection in ASP.NET Core

Similar questions' answers in Stackoverflow: https://stackoverflow.com/a/56058498/8810311, https://stackoverflow.com/a/56278027/8810311

like image 181
Ramil Aliyev Avatar answered Oct 19 '22 14:10

Ramil Aliyev