【问题标题】:Dependency Injection with inheritance依赖注入与继承
【发布时间】:2017-09-12 06:19:29
【问题描述】:

我正在使用带有 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;
    }
}

为确保用户不会发送无效的偏移量/限制,我构建了以下验证器:

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 中。

最后,我需要为当前 TRequest 和所有母类(即:PaginatedQuery)注入验证器。

这是可以实现的吗?

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

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

编辑:我忘了提到我正在使用 AutoFac

【问题讨论】:

  • AutoFac 是否提供了一些方法来检查已注册的类型? AbstractValidator&lt;T&gt; 是否实现 IValidator&lt;T&gt;

标签: c# dependency-injection autofac fluentvalidation mediatr


【解决方案1】:

Autofac 解析 ValidationBehavior&lt;GetCountriesQuery, GetCountriesResponse&gt; 时,您希望它解析 IValidator&lt;GetCountriesQuery&gt; 而只有 IValidator&lt;PaginatedQuery&gt; 被注册并且 IValidator&lt;PaginatedQuery&gt; 不能分配给 IValidator&lt;GetCountriesQuery&gt;

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

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

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

错误 CS0266:无法将类型 IValidator&lt;PaginatedQuery&gt; 隐式转换为 IValidator&lt;GetCountriesQuery&gt;。存在显式转换(您是否缺少演员表?)

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

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

public interface IValidator<in TRequest> { }

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

builder.RegisterSource(new ContravariantRegistrationSource());

【讨论】:

  • 伟大的西里尔感谢您的意见。不知道如何在 Autofac 中允许逆变!
  • 更具体地说,在带有 autofac 的 asp.net core 2.0 中,您只需要创建新方法 public void ConfigureContainer(ContainerBuilder builder)。然后只需添加 Cyril 提到的那一行就可以了!
猜你喜欢
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-24
  • 2021-11-05
  • 2021-04-16
  • 2015-09-03
  • 1970-01-01
相关资源
最近更新 更多