Asp.Net 核心依赖注入 ValidateOnBuild 无法正常工作
Asp.Net Core Dependency Injection ValidateOnBuild not works properly
我在 .NET 5 中有一个带有 RazorPages 的项目,我设置此代码以验证 Progam.cs 文件中的依赖注入:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseDefaultServiceProvider(options =>
{
options.ValidateOnBuild = true;
options.ValidateScopes = true;
})....
我忘记注册注入我页面的服务,所以我本以为当我尝试启动应用程序时,错误页面会显示此类问题,但我不明白为什么它没有不会发生,因为例如如果我不注册 ILocalizerService 会发生这种情况:
这是我的 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....
}
这是我的服务:
[PublicAPI]
public class NcsService : INcsService
{
private readonly IHttpClientFactory _httpClientFactory;
public NcsService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
// Other code...
}
我只注册了IHttpClientFactory,没有注册INcsService接口和实现:
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());
希望我说清楚了。
谢谢
问题的根源是 Microsoft 的默认 IComponentActivator
实现(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。
这意味着 Blazor 不会在内置容器中注册您的页面,因此,该页面不会成为容器验证过程的一部分。
这是,IMO,Blazor 中的一个设计缺陷,因为它众所周知,并且很好理解,如果你使用的是 DI 容器,你应该让 all 你的应用程序组件通过容器管道。这是容器可以为您提供有关应用程序组件有效性的合理确定性的唯一方法。
但是,Blazor 并不是 ASP.NET 核心框架中发生这种情况的唯一部分。 ASP.NET 例如,MVC 控制器默认情况下不在容器中注册,也不从容器中解析。虽然这是可配置的,但由于这不是默认行为,ValidateOnBuild
给人一种错误的安全感。
其他容器可能有更合理的默认设置。例如,Simple Injector(我维护的容器)包含 总是 预先注册所有 MVC 控制器的扩展方法。通过 Blazor 集成,similar things 发生了。
如果您坚持使用内置容器,最好确保从容器中解析所有组件。使用 MVC 这很容易,因为您可以简单地调用 AddControllersAsServices
。不幸的是,对于 Blazor,这要困难得多,因为不存在 AddComponentsAsServices
这样的方法。这意味着您必须创建一个回调容器的自定义 IComponentActivator
。但是,对于 Microsoft 创建的所有 Blazor 组件,您可能不得不回退到使用 Activator.CreateInstance
的原始行为,因为使用反射查找和注册它们可能要困难得多。有关如何创建此类自定义组件激活器和注册应用程序 Blazor 组件的灵感,请查看提供的代码 here.
我在 .NET 5 中有一个带有 RazorPages 的项目,我设置此代码以验证 Progam.cs 文件中的依赖注入:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseDefaultServiceProvider(options =>
{
options.ValidateOnBuild = true;
options.ValidateScopes = true;
})....
我忘记注册注入我页面的服务,所以我本以为当我尝试启动应用程序时,错误页面会显示此类问题,但我不明白为什么它没有不会发生,因为例如如果我不注册 ILocalizerService 会发生这种情况: 这是我的 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....
}
这是我的服务:
[PublicAPI]
public class NcsService : INcsService
{
private readonly IHttpClientFactory _httpClientFactory;
public NcsService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
// Other code...
}
我只注册了IHttpClientFactory,没有注册INcsService接口和实现:
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());
希望我说清楚了。
谢谢
问题的根源是 Microsoft 的默认 IComponentActivator
实现(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。
这意味着 Blazor 不会在内置容器中注册您的页面,因此,该页面不会成为容器验证过程的一部分。
这是,IMO,Blazor 中的一个设计缺陷,因为它众所周知,并且很好理解,如果你使用的是 DI 容器,你应该让 all 你的应用程序组件通过容器管道。这是容器可以为您提供有关应用程序组件有效性的合理确定性的唯一方法。
但是,Blazor 并不是 ASP.NET 核心框架中发生这种情况的唯一部分。 ASP.NET 例如,MVC 控制器默认情况下不在容器中注册,也不从容器中解析。虽然这是可配置的,但由于这不是默认行为,ValidateOnBuild
给人一种错误的安全感。
其他容器可能有更合理的默认设置。例如,Simple Injector(我维护的容器)包含 总是 预先注册所有 MVC 控制器的扩展方法。通过 Blazor 集成,similar things 发生了。
如果您坚持使用内置容器,最好确保从容器中解析所有组件。使用 MVC 这很容易,因为您可以简单地调用 AddControllersAsServices
。不幸的是,对于 Blazor,这要困难得多,因为不存在 AddComponentsAsServices
这样的方法。这意味着您必须创建一个回调容器的自定义 IComponentActivator
。但是,对于 Microsoft 创建的所有 Blazor 组件,您可能不得不回退到使用 Activator.CreateInstance
的原始行为,因为使用反射查找和注册它们可能要困难得多。有关如何创建此类自定义组件激活器和注册应用程序 Blazor 组件的灵感,请查看提供的代码 here.