使用ASP.NET Core Microsoft.Extensions.DependencyInjection注册部分封闭的通用类型

sup*_*jos 4 c# generics dependency-injection .net-core asp.net-core

在依赖于Microsoft.Extensions.DependencyInjection 1.1.1的ASP.NET Core 1.1.2 Web项目中,我试图通过其实现和接口注册一个通用的FluentValidation验证器。在运行时,在主机构建期间,出现以下异常:

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
Run Code Online (Sandbox Code Playgroud)

这是(通用的,非抽象的)验证器类:

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
Run Code Online (Sandbox Code Playgroud)

(不确定显示WithUser<T>类很重要,它只包含a Guid UserId和a T Content)。

注册的过程如下:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在运行时,这些变量保持:

+ 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}
Run Code Online (Sandbox Code Playgroud)

这似乎是正确的。

我也在SO上寻找类似的问题,但是它们并没有真正的联系,因为它们与泛型实现有关,但这是一个具体的问题。

我已逐步完成DependencyInjection模块代码,并在此处引发异常。出于某种原因,serviceTypeInfo.IsGenericTypeDefinition返回假的(不应该说是真实的,因为接口类型是通用的?)等等其他分支取。在那里,实现类型将true返回为implementationTypeInfo.IsGenericTypeDefinition,因此您有例外。

运行时值为:

+ 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}
Run Code Online (Sandbox Code Playgroud)

摆弄时,我尝试仅注册实现,而不注册接口+实现,但该异常消失了。但是我想注入接口,如果可能的话,不要注入实现。

我究竟做错了什么?TA

Ste*_*ven 5

您试图做的是映射一个部分封闭的类型(即IValidator<WithUser<T>>),. NET Core的DI容器不支持这种类型。当涉及泛型类型时,.NET Core容器是一种非常简单,简单的实现。它无法处理以下内容:

  • 通用类型约束
  • 部分封闭型
  • 通用类型参数少于抽象的实现
  • 泛型类型参数的实现顺序与抽象顺序不同的实现
  • 将泛型类型参数嵌套为部分封闭的抽象的类型参数的一部分(这是您的确切情况)
  • 奇怪的重复模板模式

如果您需要这种行为,并且想要继续为此使用内置容器,则必须手动注册每个封闭的实现:

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
Run Code Online (Sandbox Code Playgroud)

如果这导致组成根无法维护,则应选择其他容器。我知道的唯一真正完全支持这些通用设计的DI容器是Simple Injector

使用Simple Injector,您可以简单地进行以下注册:

container.Register(
    typeof(IValidator<>),
    typeof(WithUserCommandValidator<>),
    Lifestyle.Scoped);
Run Code Online (Sandbox Code Playgroud)

  • Autofac还能够注册以下开放类型:http://autofaccn.readthedocs.io/en/latest/register/registration.html#open-generic-components。由于使用命令和查询模式,因此我们严重依赖此功能。 (3认同)