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

By FoxLearn 3/13/2025 2:44:26 AM   33
When calling BuildServiceProvider() in your application, you may encounter this 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 common situations where BuildServiceProvider() might be called to resolve services manually. Below are the scenarios and solutions for addressing this issue.

Scenario 1 - Manually Resolving Dependencies

If you’re using BuildServiceProvider() to resolve dependencies manually, your code might look like this:

// Adding services
var hostAppLifetime = builder.Services.BuildServiceProvider().GetService<IHostApplicationLifetime>();
var loggerService = new DatabaseLoggerService(hostAppLifetime);
// Additional initialization code

Solution: Instead of calling BuildServiceProvider(), you should use the overloads of AddSingleton, AddTransient, or AddScoped that accept a ServiceProvider parameter. This allows you to resolve services using the provided ServiceProvider without creating duplicates.

// Adding services
builder.Services.AddSingleton<ILoggerService>(sp =>
{
	var hostAppLifetime = sp.GetService<IHostApplicationLifetime>();
	return new DatabaseLoggerService(hostAppLifetime);
});
// Additional initialization code

In versions prior to .NET 6, you would implement this logic in Startup.ConfigureServices().

Scenario 2 - Resolving a Service to Get Dynamic Values for Another Service

In this case, you might be resolving a service to get dynamic values (e.g., from a database) and passing those values to another service you’re registering. This could involve the options pattern or similar configurations.

// 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();
});
// Additional initialization code

Solution: To address this, you should stick to the options pattern, which is designed to solve the issue of fetching dynamic values from registered services. Instead of calling BuildServiceProvider(), you can use the Configure<T>() overload that gives you access to the required service, like so:

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

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

This way, you access the resolved service directly when configuring your options, eliminating the need for BuildServiceProvider().

In versions prior to .NET 6, you would implement this logic in Startup.ConfigureServices().

By following these solutions, you can avoid creating unnecessary copies of singleton services and ensure that your application uses dependency injection efficiently.