I'm having a few problems getting my FluentValidation validators working with Simple Injector.
I have a decorator with a constructor of:
public CommandHandlerValidationDecorator(
IRequestHandler<TRequest, TRepsonse> innerHandler,
IValidator<TRequest>[] validators)
{
_decoratedHandler = innerHandler;
_validators = validators;
}
The issue is with the 2nd parameter, IValidator<TRequest>[] validators
.
Guided by the error messages I was getting, I did my configuration as follows:
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<,>));
This worked fine, up until I changed the lifetime scoping to per web request:
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);
It looks as though you cannot scope RegisterCollection to per web request and that becomes a problem because an exception is thrown owing to a lifestyle mismatch:
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.
Perhaps I am trying to force something which is bad practice?
What you're seeing is described here in the documentation:
Simple Injector preserves the lifestyle of instances that are returned from an injected
IEnumerable<T>
,ICollection<T>
,IList<T>
,IReadOnlyCollection<T>
andIReadOnlyList<T>
instances. In reality you should not see the the injectedIEnumerable<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 (theIEnumerable<T>
orICollection<T>
itself is a singleton) and each time you iterate theIEnumerable<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.
So you've got two options:
IEnumerable<T>
, ICollection<T>
, IList<T>
, IReadOnlyCollection<T>
or IReadOnlyList<T>
.