25

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);
    }
punkouter
  • 4,920
  • 15
  • 64
  • 106
  • 4
    First, why are you building provider? This might be an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Can you reformat the question so we get a clearer picture of the current problem and what you are **actually** trying to do? – Nkosi Nov 22 '19 at 18:03
  • Im not sure. I guess I already have one and perhaps that is creating another ? – punkouter Nov 22 '19 at 21:45
  • What do you mean by `WARNING IS HERE`? Please provide details about the warning. Show us the text of the warning. Is this a compiler warning? A warning from some code analysis plugin? If so, which one? Is this a runtime exception? Show us all relevant details of the exception (message, type, stack trace, inner exceptions). – Steven Nov 24 '19 at 21:37
  • @punkouter "What does it want me to change": Don't build the service provider manually by invoking`BuildServiceProvider()`. This method should be invoked by Host only once. Duplicate service provider might lead to some unexpected bugs. – itminus Nov 25 '19 at 02:06
  • 1
    The warning is the title. I guess IServiceCollection is where I should put this logger somehow? I need to understand better IServiceCollection vs. a ServiceProvider. – punkouter Nov 25 '19 at 14:06
  • @punkouter Did you read my answer, that I did update it ? https://stackoverflow.com/a/59836362/8810311 – Ramil Aliyev Jan 30 '21 at 20:41
  • Ok.. I wish I could remember what I was doing ..its been so long – punkouter Feb 01 '21 at 15:04

2 Answers2

20

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

Ramil Aliyev
  • 3,414
  • 1
  • 22
  • 40
  • 32
    Calling this method from another function is not the right solution. You should avoid calling it from ANYWHERE in your code. This is just removing the warning. – Adys Feb 19 '20 at 10:26
  • @Adys I agree with you my friend, I mentioned this solution is remove warning :) I thinks correct version is to call ServiceProvider of app, which app is IApplicationBuilder in Configure method – Ramil Aliyev Feb 19 '20 at 13:38
  • 1
    To remove the warning, you could just suppress it. – Joshua VdM Dec 01 '20 at 15:47
  • I used var serviceProvider = services.BuildServiceProvider(); var logger = serviceProvider.GetService>(); services.AddSingleton(typeof(ILogger), logger); in the function. I was getting an additional singleton warning before – Golden Lion Dec 10 '21 at 17:10
  • what is RegisterSerilogLogger? should this be used instead of addSingleton – Golden Lion Dec 10 '21 at 17:11
  • for asp core 6 you can use `app.Services` instead of `app.ApplicationServices` – Ahmed Mohammed May 22 '22 at 19:29
-2

The ONLY purpose of calling 'BuildServiceProvider' is to get a service provider instance,

To remove this call and still be able to use IServiceProvider, change Configure method to get it as parameter:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider provider)
Adys
  • 125
  • 1
  • 7
  • You needn't inject IServiceProvider because app.ApplicationServices is IServiceProvider . – Ramil Aliyev Feb 24 at 11:48 – Ramil Aliyev Feb 28 '20 at 05:42