I am trying to automate the validation of my view models, I know I can just add a an attribute to specify my validation but there is an option to set up a factory to automate all that, I looked at: this answer and came up with this using simple injector 3.1:
public class CustomValidatorFactory:ValidatorFactoryBase
{
private readonly Container siContainer;
public CustomValidatorFactory(Container siContainer)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
this.siContainer = siContainer;
this.siContainer.Register(typeof(IValidator<>), assemblies);
}
public override IValidator CreateInstance(Type validatorType)
{
//var instances = siContainer.GetAllInstances(validatorType);
var implementation = ((IServiceProvider)siContainer).GetService(validatorType);
var validatorInstance = implementation != null ? (implementation as IValidator) : null;
return validatorInstance;
}
}
Then the view model can be something like
public class Person {
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleFor(x => x.Id).NotNull();
RuleFor(x => x.Name).Length(0, 10);
RuleFor(x => x.Email).EmailAddress();
RuleFor(x => x.Age).InclusiveBetween(18, 60);
}
}
However the implementation variable is always null, I've also tried RegisterCollection but still have the same issue, seems like simple injector does not know how to resolve IValidator when the validator inherits from AbstractValidator(This is the class that implements IValidator)
Register the Fluent Validation Factory in Simple Injector like this:
public class ApplicationValidatorFactory : IValidatorFactory
{
private readonly Container _container;
/// <summary>The constructor of the factory.</summary>
/// <param name="container">The Simple Injector Container</param>
public ApplicationValidatorFactory(Container container)
{
_container = container;
}
/// <summary>Gets the validator for the specified type.</summary>
public IValidator<T> GetValidator<T>()
{
return _container.GetInstance<IValidator<T>>();
}
/// <summary>Gets the validator for the specified type.</summary>
public IValidator GetValidator(Type type)
{
var validator = typeof(IValidator<>).MakeGenericType(type);
return (IValidator)_container.GetInstance(validator);
}
}
And then in your Composition Root register it for ASP.NET MVC:
// Register the validators and factory
var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
container.Register<IValidatorFactory, ApplicationValidatorFactory>(Lifestyle.Singleton);
container.Register(typeof(IValidator<>), assemblies);
// Register Simple Injector validation factory in FV
FluentValidationModelValidatorProvider.Configure(provider => {
provider.ValidatorFactory = new ApplicationValidatorFactory(container);
provider.AddImplicitRequiredValidator = false;
}
);
The FluentValidationModelValidatorProvider
is found in the FluentValidation.MVC
integration package. Remember to get the one for the version of MVC you are running.
UPDATE
You can register an empty validator for the object that does not have an validator like:
/// <summary>
/// Adds an unregistered type resolution for objects missing an IValidator.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
internal sealed class ValidateNothingDecorator<T> : AbstractValidator<T>
{
// I do nothing :-)
}
// Add unregistered type resolution for objects missing an IValidator<T>
// This should be placed after the registration of IValidator<>
container.RegisterConditional(typeof(IValidator<>), typeof(ValidateNothingDecorator<>), Lifestyle.Singleton, context => !context.Handled);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With