31

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
  • 5,170
  • 15
  • 71
  • 116
  • 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 007 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

19

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

Calling BuildServiceProvider creates a second container, which can create torn singletons and cause references to object graphs across multiple containers. ASP.NET Core will build the ServiceProvider automatically at runtime, therefore don't call the BuildServiceProvider() manually.

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 007
  • 4,437
  • 2
  • 31
  • 47
  • 40
    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 007 Feb 19 '20 at 13:38
  • 3
    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
  • 1
    for asp core 6 you can use `app.Services` instead of `app.ApplicationServices` – Ahmed Mohammed May 22 '22 at 19:29
  • I think this is worse than just directly suppressing the warning, because when it is suppressed, the intention is clear by looking at the code. You can realize easily that a developer thought that it is necessary to build a copy of singleton services at that time. Maybe, another developer may see it and fix it with a better approach. However, masking the issue by tricking the compiler (like in this answer) will bury the real problem forever and may lead to bugs which are difficult to debug. – MÇT Jul 06 '22 at 13:19
  • @MÇT thanks for your comment, I updated my answer at UPDATED 24.01.2021. Please read it – Ramil Aliyev 007 Jul 08 '22 at 06:54
  • @RamilAliyev unfortunately, your update does not apply to this question, because the question needs it in `ConfigureServices` method, not `Configure` method. `ConfigureServices` does not have an `IApplicationBuilder app` parameter. – MÇT Jul 08 '22 at 21:49
  • @MÇT Microsoft says: "Avoid calls to BuildServiceProvider in ConfigureServices. Calling BuildServiceProvider typically happens when the developer wants to resolve a service in ConfigureServices." See more: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1#recommendations – Ramil Aliyev 007 Jul 09 '22 at 13:28
  • You should delete the part about simply moving it into a function. -1 – Jeppe Jun 05 '23 at 13:31
  • @Jeppe I think you didn't read my answer. I updated it on 24.01.2021. You can use Configure method – Ramil Aliyev 007 Jun 07 '23 at 09:11
  • I did read it, hence "the part". – Jeppe Jun 08 '23 at 05:38
-1

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
  • 135
  • 1
  • 7
  • You needn't inject IServiceProvider because app.ApplicationServices is IServiceProvider . – Ramil Aliyev Feb 24 at 11:48 – Ramil Aliyev 007 Feb 28 '20 at 05:42
  • @RamilAliyev, in most cases you are correct. In some instance, I have had the need to use an injected service to register another service (working with code out of my control). Once you call WebApplicationBuilder.Build(), your application has been built and you can no longer register services. The solution above would work in that case. – Saturn K Nov 30 '22 at 22:12