简体   繁体   中英

Asp.Net Core Dependency Injection ValidateOnBuild not works properly

I have a project in .NET 5 with RazorPages, I set this code to validate the Dependecy Injection in the Progam.cs file:

public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseDefaultServiceProvider(options =>
            {
                options.ValidateOnBuild = true;
                options.ValidateScopes = true;
            })....

I forgot to register the service that is injected into my page, so I would have expected that when I try to start the app an error page would show this kind of problem, but I don't understand why it doesn't happen, because for example in case I don't register ILocalizerService this happens: This is my RazorPage:

public class SignupModel : IdentityPageModel
{
    [BindProperty]
    public Models.Account.Signup Signup { get; set; }

    private readonly CustomUserManager _userManager;
    private readonly ILogger<SignupModel> _logger;
    private readonly INcsService _ncsService;

    public SignupModel(CustomUserManager userManager, 
        ILogger<SignupModel> logger,
        INcsService ncsService) : base(localizerService)
    {
        Guard.Against.Null(userManager, nameof(userManager));

        Guard.Against.Null(logger, nameof(logger));
        Guard.Against.Null(ncsService, nameof(ncsService));
        
        _userManager = userManager;
        _logger = logger;
        _ncsService = ncsService;
    }

    // Other code....
}

This is my service:

[PublicAPI]
public class NcsService : INcsService
{
    private readonly IHttpClientFactory _httpClientFactory;

    public NcsService(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    // Other code...
}

I have only registered IHttpClientFactory but not INcsService interface and implementation:

services.AddHttpClient(nameof(NcsService), client =>
{
    client.BaseAddress = new Uri(ncsSettings.BaseUri);
    client.DefaultRequestHeaders.Add("x-functions-key", ncsSettings.ApiKey);
    client.DefaultRequestHeaders.Add("x-app-name", "TSID");

}).AddHeaderPropagation(options =>
{
    options.Headers.Add("x-request-id");
    options.Headers.Add("x-correlation-id");
})
.AddPolicyHandler(GetRetryPolicy());

I hope I was clear.
Thank you

The root of the issue is Microsoft's default IComponentActivator implementation (the DefaultComponentActivator ). The Component Activator is in control of creating your Razor Pages, but the built-in behavior does not request those pages from the built-in container. Instead, it just creates them using Activator.CreateInstance .

This means that Blazor does not register your pages in the built-in container and because of that, the page will not be part of the container's verification process.

This is, IMO, a design flaw in Blazor, because it well known, and well understood that, in case you are using a DI Container, you should let all your application components go through the container pipeline. That's the only way that the container can give you a reasonable amount of certainty about the validity of your application components.

Blazor, however, is not the only part of the ASP.NET Core framework where this happens. ASP.NET MVC Controllers, for instance, by default aren't registered in the container, and aren't resolved from the container. This is configurable though, but since this is not the default behavior, the ValidateOnBuild gives a false sense of security.

Other containers might have a more sensible default. Simple Injector, for instance, (the container that I maintain) contains extension methods that always register all MVC controllers up front. And with the Blazor integration, similar things happen.

If you stick with the built-in container, it would be good to ensure all components are resolved from the container. With MVC this is easy, because you can simply call AddControllersAsServices . With Blazor, unfortunately, this is much more difficult, because there exists no such method as AddComponentsAsServices . This means that you have to create a custom IComponentActivator that calls back into the container. But still, you'll likely have to fallback to the original behavior using Activator.CreateInstance for all Blazor Components that are created by Microsoft, because it might be much harder to find and register them using reflection. For inspiration on how to create such custom Component Activator and register your application Blazor components, take a look at the code presented here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM