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

问题

在依赖于 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

这是(泛型的,非抽象的)验证器类:

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<>);

// 这是用于顶级命令验证器的,由接口注入
services.AddScoped(someInterface, someImplementation);

// 这是嵌套的验证器,由实现注入
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 模块代码,这里抛出了异常。 出于某种原因,serviceTypeInfo.IsGenericTypeDefinition 返回 false(这不应该是真的,因为接口类型是通用的吗?)因此采用了 else 分支。 相反,实现类型返回 trueimplementationTypeInfo.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}

在调试时,我尝试只注册实现,而不注册接口+实现,然后那个异常就消失了。 但如果可能的话,我想注入接口,而不是实现。

答案

您要做的是映射一个部分封闭的类型(即 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

如果这导致无法维护的组合根,您应该选择一个不同的容器。 据我所知,唯一完全支持这些泛型设计的 DI 容器是 Simple Injector

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

container.Register(
    typeof(IValidator<>),
    typeof(WithUserCommandValidator<>),
    Lifestyle.Scoped);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dependency Injection Principles, Practices, and Patterns teaches you to use DI to reduce hard-coded dependencies between application components. You'll start by learning what DI is and what types of applications will benefit from it. Then, you'll work through concrete scenarios using C# and the .NET framework to implement DI in your own projects. As you dive into the thoroughly-explained examples, you'll develop a foundation you can apply to any of the many DI libraries for .NET and .NET Core. Purchase of the print book includes a free eBook in PDF, Kindle, and ePub formats from Manning Publications. About the Technology Dependency Injection (DI) is a great way to reduce tight coupling between software components. Instead of hard-coding dependencies, such as specifying a database driver, you make those connections through a third party. Central to application frameworks like ASP.NET Core, DI enables you to better manage changes and other complexity in your software. About the Book Dependency Injection Principles, Practices, and Patterns is a revised and expanded edition of the bestselling classic Dependency Injection in .NET. It teaches you DI from the ground up, featuring relevant examples, patterns, and anti-patterns for creating loosely coupled, well-structured applications. The well-annotated code and diagrams use C# examples to illustrate principles that work flawlessly with modern object-oriented languages and DI libraries. What's Inside Refactoring existing code into loosely coupled code DI techniques that work with statically typed OO languages Integration with common .NET frameworks Updated examples illustrating DI in .NET Core About the Reader For intermediate OO developers.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值