DEV Community

Alex Khanuckaev
Alex Khanuckaev

Posted on

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);
    }
}

Enter fullscreen mode Exit fullscreen mode

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);
};
Enter fullscreen mode Exit fullscreen mode

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,
    };
}
Enter fullscreen mode Exit fullscreen mode

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.

Dev screnshot
Prod screenshot

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.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (2)

Collapse
 
stevsharp profile image
Spyros Ponaris

You can also check the environment programmatically and handle validation or different behaviors for different environments:

public void ConfigureServices(IServiceCollection services)
{
// Only validate services in Development
if (Environment.IsDevelopment())
{
services.BuildServiceProvider(validateScopes: true);
}
}

learn.microsoft.com/en-us/dotnet/a...

Collapse
 
alechka profile image
Alex Khanuckaev

Thanks for your answer. I've checked that. services.BuildServiceProvider(validateScopes: true) despite looking like it forces scope validation, does not. To really enforce it you have to do this:

builder.Services.BuildServiceProvider(new ServiceProviderOptions() { ValidateOnBuild = true, ValidateScopes = true });
Enter fullscreen mode Exit fullscreen mode

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay