在依赖于 Microsoft.Extensions.DependencyInjection 1.1.1 的 ASP.NET Core 1.1.2 Web 项目中,我正在尝试注册一个通用的 FluentValidation验证器及其实现和接口(interface)。在运行时,在主机构建期间,我遇到了以下异常:
An unhandled exception of type 'System.ArgumentException' occurred in Microsoft.Extensions.DependencyInjection.dll
Cannot instantiate implementation type 'MyProj.Shared.Api.WithUserCommandValidator`1[T]' for service type 'FluentValidation.IValidator`1[MyProj.Shared.Model.WithUser`1[T]]'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceTable..ctor(IEnumerable`1 descriptors)
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, Boolean validateScopes)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services)
at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
at MyProj.Program.Main(String[] args) in X:\MySolution\src\MyProj\Program.cs:line 31
这是(通用的,非抽象的)验证器类:
public class WithUserCommandValidator<TCommand> : AbstractValidator<WithUser<TCommand>>
where TCommand : ICommand
{
public WithUserCommandValidator(IValidator<TCommand> commandValidator)
{
RuleFor(cmd => cmd).NotNull();
RuleFor(cmd => cmd.UserId).NotEqual(Guid.Empty);
RuleFor(cmd => cmd.Content).NotNull()
.SetValidator(commandValidator);
}
}
(不确定显示 WithUser<T>
类是否重要,它只包含一个 Guid UserId
和一个 T Content
)。
注册过程是这样的:
someInterface = typeof(FluentValidation.IValidator<WithUser<>>);
someImplementation = typeof(Shared.Api.WithUserCommandValidator<>);
// this is for top-level command validators, injected by interface
services.AddScoped(someInterface, someImplementation);
// this is for nested validators, injected by implementation
services.AddScoped(someImplementation);
并且在运行时这些变量保持:
+ someInterface {FluentValidation.IValidator`1[MyProj.Shared.Model.WithUser`1[TCommand]]} System.Type {System.RuntimeType}
+ someImplementation {MyProj.Shared.Api.WithUserCommandValidator`1[TCommand]} System.Type {System.RuntimeType}
这似乎是正确的。
我也在 SO 上寻找类似的问题和问题,但它们并不真正相关,因为它们是关于通用实现的,而这是一个具体的实现。
我单步执行了 DependencyInjection 模块代码,但抛出了异常 here .出于某种原因,serviceTypeInfo.IsGenericTypeDefinition
返回 false(不应该是 true,因为接口(interface)类型是通用的吗?)所以采用 else 分支。相反,实现类型返回 true 到 implementationTypeInfo.IsGenericTypeDefinition
,所以你有异常(exception)。
运行时值是:
+ serviceTypeInfo {FluentValidation.IValidator`1[MyProj.Shared.Model.WithUser`1[TCommand]]} System.Reflection.TypeInfo {System.RuntimeType}
+ implementationTypeInfo {MyProj.Shared.Api.WithUserCommandValidator`1[TCommand]} System.Reflection.TypeInfo {System.RuntimeType}
在摆弄时,我尝试只注册实现,而不注册接口(interface)+实现,然后那个异常就消失了。但如果可能的话,我想注入(inject)接口(interface),而不是实现。
我做错了什么?助教
最佳答案
您要做的是映射一个部分封闭的类型(即 IValidator<WithUser<T>>
),这是 .NET Core 的 DI 容器不支持的。对于泛型类型,.NET Core 容器是一个非常简约、简单的实现。它无法处理类似的事情:
- 通用类型约束
- 部分封闭类型
- 具有比抽象更少泛型类型参数的实现
- 以不同于抽象的顺序使用泛型类型参数的实现
- 将泛型类型参数嵌套为部分封闭抽象的类型参数的一部分(这是您的确切情况)
- Curiously recurring template pattern
如果您需要此行为并且想为此继续使用内置容器,您将必须手动注册每个关闭的实现:
AddScoped(typeof(IValidator<WithUser<Cmd1>>),typeof(WithUserCommandValidator<Cmd1>));
AddScoped(typeof(IValidator<WithUser<Cmd2>>),typeof(WithUserCommandValidator<Cmd2>));
AddScoped(typeof(IValidator<WithUser<Cmd3>>),typeof(WithUserCommandValidator<Cmd3>));
// etc
如果这会导致不可维护的组合根,您应该选择一个不同的容器。我所知道的唯一完全支持这些通用设计的 DI 容器是 Simple Injector .
使用 Simple Injector,您可以简单地进行以下注册:
container.Register(
typeof(IValidator<>),
typeof(WithUserCommandValidator<>),
Lifestyle.Scoped);
关于c# - 使用 ASP.NET Core Microsoft.Extensions.DependencyInjection 注册部分封闭的泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44987331/