具有继承的依赖注入

Dependency Injection with inheritance

我正在使用 MediatR 构建的管道。我正在添加一个旨在验证查询和命令的简单行为:

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<Result>
    where TResponse : Result
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators ?? Enumerable.Empty<IValidator<TRequest>>();
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
    {
      (...)
    }
}

目前我的大部分查询都继承自PaginatedQuery。

public abstract class PaginatedQuery : IQuery
{
    public int Offset { get; set; } = 0;
    public int Limit { get; set; } = 25;
}

示例:

public class GetCountriesQuery : PaginatedQuery
{
    public GetCountriesQuery(PaginatedInput input)
    {
        Limit = input.Limit;
        Offset = input.Offset;
    }
}

为了确保用户不会发送无效的 Offset/Limit,我构建了以下验证器:

public class PaginatedQueryValidator : AbstractValidator<PaginatedQuery>
{
    public PaginatedQueryValidator()
    {
        RuleFor(p => p.Offset)
            .GreaterThanOrEqualTo(0)
            .WithMessage("Offset must be greater or equal to 0");
        RuleFor(p => p.Limit)
            .GreaterThan(0)
            .WithMessage("Limit must be greater than 0");
    }
}

不幸的是,即使 TRequest 继承自 PaginatedQuery,该验证器也没有注入到我的 ValidationBehavior 中。

In the end, I would need to inject validators for the current TRequest AND also for all mother classes (ie: PaginatedQuery).

这是可以实现的吗?

注意:为了注入验证器,我扫描了所有实现 IValidator<> 的类型(使用 Scrutor)

        services.Scan(x =>
            x.FromAssembliesOf(typeof(Startup))
                .AddClasses(c =>
                    c.AssignableToAny(typeof(IValidator<>)))
                .AsImplementedInterfaces()
        );

编辑:我忘了说我正在使用 AutoFac

Autofac 解析 ValidationBehavior<GetCountriesQuery, GetCountriesResponse> 时,您希望它解析 IValidator<GetCountriesQuery> 而只有 IValidator<PaginatedQuery> 被注册并且 IValidator<PaginatedQuery> 不可分配给 IValidator<GetCountriesQuery>

您可以尝试使用以下 C# 语句

IValidator<GetCountriesQuery> i = (IValidator<PaginatedQuery>)null;

C#编译器会给你这个错误信息

Error CS0266 : Cannot implicitly convert type IValidator<PaginatedQuery> to IValidator<GetCountriesQuery>. An explicit conversion exists (are you missing a cast?)

为了让它工作,C#4 为泛型类型参数引入了协变和逆变。它允许将 IValidator<Base> 转换为 IValidator<Derived> 或相反。有关详细信息,请参阅 MSDN 上的 covariance and contravariance

在您的情况下,您需要可以使用 in 关键字

声明的逆变
public interface IValidator<in TRequest> { }

为了使 Autofac 与逆变一起工作,您必须注册 ContravariantRegistrationSource 注册源:

builder.RegisterSource(new ContravariantRegistrationSource());