dev-resources.site
for different kinds of informations.
Dependency injection validation error in ASP.NET Core projects
Recently I was digging into some dotnet project and while (days of) debugging realised, that some of singleton projects actually depends on the scoped ones.
Problem
Code example for better understanding:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddScoped<Scoped>();
builder.Services.AddSingleton<Singleton>();
var app = builder.Build();
class Scoped
{
public string Test { get; set; } = "It works! But shouldn't";
}
class Singleton(Scoped sc)
{
public void Print()
{
Console.WriteLine(sc.Test);
}
}
This code started and didn't produce any errors. But of course, some services are scoped for a reason (EF Context for example), so when they don't you have a lot of really hard figure out bugs.
So what's happening?
Let's dive deeper inside builder.Build() here. It's actually using Build method from HostApplicationBuilder class. There is a code inside it:
ServiceProviderOptions? serviceProviderOptions = null;
if (!settings.DisableDefaults)
{
HostingHostBuilderExtensions.ApplyDefaultAppConfiguration(_hostBuilderContext, Configuration, settings.Args);
HostingHostBuilderExtensions.AddDefaultServices(_hostBuilderContext, Services);
serviceProviderOptions = HostingHostBuilderExtensions.CreateDefaultServiceProviderOptions(_hostBuilderContext);
}
_createServiceProvider = () =>
{
// Call _configureContainer in case anyone adds callbacks via HostBuilderAdapter.ConfigureContainer<IServiceCollection>() during build.
// Otherwise, this no-ops.
_configureContainer(Services);
return serviceProviderOptions is null ? Services.BuildServiceProvider() : Services.BuildServiceProvider(serviceProviderOptions);
};
So yep, Services.BuildServiceProvider() without parameters doesn't validate services by default. But there are some options that comes from HostingHostBuilderExtensions. Ok, sometimes I like Microsoft naming. When you see something named as HostingHostBuilderExtensions, you can be absolutely sure, that things are completely screwed, and you are in a really deep water.
But this surprisingly is the end of the long journey. Here it is:
internal static ServiceProviderOptions CreateDefaultServiceProviderOptions(HostBuilderContext context)
{
bool isDevelopment = context.HostingEnvironment.IsDevelopment();
return new ServiceProviderOptions
{
ValidateScopes = isDevelopment,
ValidateOnBuild = isDevelopment,
};
}
So, the actual difference is Environment. Some folks in asp.net core development team decided to reduce building time of dependency injector containers by making validation dependant on, in fact, an environment variable. And havenโt provided any documentation.
TL;DR
Service validation in ASP.NET Core apps depends on environment. Guys in Microsoft made some optimizations, so validations works only if you are running your app in development environment. So be sure to set ASPNETCORE_ENVIRONMENT or DOTNET_ENVIRONMENT to Develompent when you are debugging your code.
Featured ones: