具有继承的依赖注入
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());
我正在使用 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>
toIValidator<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());