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

When you try to call BuildServiceProvider(), you 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

You’re trying to call services.BuildServiceProvider() in order to manually resolve dependencies. Your code may look something like this:

//rest of adding services

var hostAppLifetime = builder.Services.BuildServiceProvider().GetService<IHostApplicationLifetime>();
var loggerService = new DatabaseLoggerService(hostAppLifetime);

//rest of init code
Code language: C# (cs)

Note: Before .NET 6, this is in Startup.ConfigureServices().

Solution

Instead of using BuildServiceProvider(), use the overload of AddSingleton/Transient/Scoped that has a ServiceProvider parameter. Use the passed in ServiceProvider’s GetService(). Here’s an example:

//Rest of adding services

builder.Services.AddSingleton<ILoggerService>(sp =>
{
	var hostAppLifetime = sp.GetService<IHostApplicationLifetime>();
	return new DatabaseLoggerService(hostAppLifetime);
});

//Rest of init code
Code language: C# (cs)

Note: Before .NET 6, do this in Startup.ConfigureServices().

Scenario 2 – You’re resolving a service to get a dynamic value for another service

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):

//rest of adding services

builder.Services.AddSingleton<ISettingsRepository, SettingsRepository>();
builder.Services.AddSingleton<IThirdPartyService, ThirdPartyService>();

builder.Services.AddOptions<Settings>().Configure(options =>
{
	options.StartAt = services.BuildServiceProvider().GetService<ISettingsRepository>().GetStartDate();
});

//Rest of init code
Code language: C# (cs)

Note: Before .NET 6, this is in Startup.ConfigureServices().

Solution

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

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

//rest of adding services

builder.Services.AddSingleton<ISettingsRepository, SettingsRepository>();
builder.Services.AddSingleton<IThirdPartyService, ThirdPartyService>();

builder.Services.AddOptions<Settings>()
	.Configure<ISettingsRepository>((options, settingsRepo) =>
	{
		options.StartAt = settingsRepo.GetStartDate();
	});

//Rest of init code
Code language: C# (cs)

Note: Before .NET 6, do this in Startup.ConfigureServices().

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

Comments are closed.