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
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.
- How to batch read with Threading.ChannelReader in C#
- How to ignore JSON deserialization errors in C#
- JsonException: A possible object cycle was detected
- JSON value could not be converted to System.String in C#
- How to use Newtonsoft in ASP.NET Core
- How to use Polly In C#
- Global exception event handlers in C#
- How to Add or overwrite a value in ConcurrentDictionary in C#