ASP0000 Calling BuildServiceProvider from application code results in an additional copy of singleton services being created

When you try to call services.BuildServiceProvider(), you’ll get the following warning:

Warning ASP0000 Calling ‘BuildServiceProvider’ from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to ‘Configure’.

There are two scenarios where you may be calling BuildServiceProvider() because you want to resolve services manually. Take a look at the scenarios and their solutions below.

Scenario 1 – You’re trying to manually resolve dependencies in Startup.ConfigureServices()

You’re probably trying to manually create an object in Startup.ConfigureServices() and need to resolve its dependencies, so you’re trying to call services.BuildServiceProvider(). Your code may look something like this:

public class Startup { //rest of class public void ConfigureServices(IServiceCollection services) { //rest of method var hostAppLifetime = services.BuildServiceProvider().GetService<IHostApplicationLifetime>(); var loggerService = new DatabaseLoggerService(hostAppLifetime); } }
Code language: C# (cs)

Solution

In order to use ServiceProvider.GetService() in Startup.ConfigureServices(), create your object using services.AddSingleton() like this:

public class Startup { //rest of class public void ConfigureServices(IServiceCollection services) { //rest of method services.AddSingleton<ILoggerService>(sp => { var hostAppLifetime = sp.GetService<IHostApplicationLifetime>(); return new DatabaseLoggerService(hostAppLifetime); }); } }
Code language: C# (cs)

This overload of AddSingleton() gives you access to the ServiceProvider object.

Scenario 2 – You’re resolving a service to get a dynamic value for another service in Startup.ConfigureServices()

You’re probably trying to get a dynamic value, such as from the database, to pass into another object that you’re registering. You may or may not be using the options pattern.

Your code may look something like this (if you’re using the options pattern):

public class Startup { //rest of class public void ConfigureServices(IServiceCollection services) { services.AddSingleton<ISettingsRepository, SettingsRepository>(); services.AddSingleton<IThirdPartyService, ThirdPartyService>(); services.AddOptions<Settings>().Configure(options => { options.StartAt = services.BuildServiceProvider().GetService<ISettingsRepository>().GetStartDate(); }); //rest of method } }
Code language: C# (cs)

Solution

First, the options pattern is a good way to solve the “fetch a dynamic value using a registered service” problem, so I would suggest using it.

Instead of calling services.BuildServiceProvider(), use the appropriate services.AddOptions().Configure<T>() overload, like this:

public class Startup { //rest of class public void ConfigureServices(IServiceCollection services) { services.AddSingleton<ISettingsRepository, SettingsRepository>(); services.AddSingleton<IThirdPartyService, ThirdPartyService>(); services.AddOptions<Settings>() .Configure<ISettingsRepository>((options, settingsRepo) => { options.StartAt = settingsRepo.GetStartDate(); }); //rest of method } }
Code language: C# (cs)

This .Configure<T>() overload gives you access to the resolved service, so you can use it to set a value on the options object.

Leave a Comment