FluentValidation 验证器和简单注入器,其中验证器作为数组注入

FluentValidation Validators and Simple Injector where Validators Injected as Array

我在让 FluentValidation 验证器与 Simple Injector 一起工作时遇到了一些问题。

我有一个带有构造函数的装饰器:

public CommandHandlerValidationDecorator(
    IRequestHandler<TRequest, TRepsonse> innerHandler, 
    IValidator<TRequest>[] validators)
{
    _decoratedHandler = innerHandler;
    _validators = validators;
}

问题出在第二个参数 IValidator<TRequest>[] validators

根据收到的错误消息,我进行了如下配置:

container.Register(typeof(FluentValidation.IValidator<>), new[] { assembly });
container.RegisterCollection(typeof(FluentValidation.IValidator<>), new[] { assembly });
container.Register(typeof(IRequestHandler<,>),new [] { assembly });
container.RegisterDecorator(typeof(IRequestHandler<,>), 
    typeof(CommandHandlerValidationDecorator<,>));

这工作正常,直到我将生命周期范围更改为每个网络请求:

container.Register(typeof(FluentValidation.IValidator<>), new[] { assembly }, 
    Lifestyle.Scoped);
container.RegisterCollection(typeof(FluentValidation.IValidator<>), new[] { assembly });
container.Register(typeof(IRequestHandler<,>),new [] { assembly }, Lifestyle.Scoped);
container.RegisterDecorator(typeof(IRequestHandler<,>), 
    typeof(CommandHandlerValidationDecorator<,>), Lifestyle.Scoped);

您似乎无法将 RegisterCollection 的范围限定为每个 Web 请求,这会成为一个问题,因为生活方式不匹配会引发异常:

Additional information: A lifestyle mismatch is encountered. CommandHandlerValidationDecorator (Web Request) depends on IValidator[] (Transient). Lifestyle mismatches can cause concurrency bugs in your application. Please see https://simpleinjector.org/dialm to understand this problem and how to solve it.

也许我试图强迫一些不好的做法?

文档中 here 描述了您所看到的内容:

Simple Injector preserves the lifestyle of instances that are returned from an injected IEnumerable<T>, ICollection<T>, IList<T>, IReadOnlyCollection<T> and IReadOnlyList<T> instances. In reality you should not see the the injected IEnumerable<T> as a collection of instances; you should consider it a stream of instances. Simple Injector will always inject a reference to the same stream (the IEnumerable<T> or ICollection<T> itself is a singleton) and each time you iterate the IEnumerable<T>, for each individual component, the container is asked to resolve the instance based on the lifestyle of that component.

Warning: In contrast to the collection abstractions, an array is registered as transient. An array is a mutable type; a consumer can change the contents of an array. Sharing the array (by making it singleton) might cause unrelated parts of your applications to fail because of changes to the array. Since an array is a concrete type, it can not function as a stream, causing the elements in the array to get the lifetime of the consuming component. This could cause lifestyle mismatches when the array wasn’t registered as transient.

所以你有两个选择:

  1. 也让数组的消费者成为瞬态,或者
  2. 将参数的自变量从数组更改为 IEnumerable<T>ICollection<T>IList<T>IReadOnlyCollection<T>IReadOnlyList<T>