Forem

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.

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

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

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay