dependency-injectionfluentvalidationfluentvalidation-2.0open-genericssimple-injector

What is the correct way to register FluentValidation with Simple Injector?


I am able to register FluentValidation AbstractValidators using a FluentValidatorFactory. However, it doesn't feel right, because not all of the IoC container registrations happen during bootstrap / composition root. Instead, the fluent validators are registered by a separate factory:

The composition root:

public class SimpleDependencyInjector : IServiceProvider
{
    public readonly Container Container;

    public SimpleDependencyInjector()
    {
        Container = Bootstrap();
    }

    internal Container Bootstrap()
    {
        var container = new Container();

        container.Register< // ...register all non-fluent-validator types, then

        container.Verify();
        return container;
    }

    public object GetService(Type serviceType)
    {
        return ((IServiceProvider)Container).GetService(serviceType);
    }
}

An abstract fluent validator factory depending only on IServiceProvider

public abstract class FluentValidatorFactory : ValidatorFactoryBase
{
    private IServiceProvider Injector { get; set; }

    protected FluentValidatorFactory(IServiceProvider injector)
    {
        Injector = injector;
    }

    public override IValidator CreateInstance(Type validatorType)
    {
        return Injector.GetService(validatorType) as IValidator;
    }
}

A fluent validator factory implementation for SimpleInjector

public class SimpleValidatorFactory : FluentValidatorFactory
{
    public SimpleValidatorFactory(SimpleDependencyInjector injector) 
        : base(injector)
    {
        var validators = AssemblyScanner.FindValidatorsInAssembly(
            Assembly.GetCallingAssembly());
        validators.ForEach(validator => 
            injector.Container.Register(
                validator.InterfaceType, validator.ValidatorType));
        injector.Container.Verify();
    }
}

SimpleInjector has good support for open generics, and all of my fluent validator classes have signatures similar to the following:

public class SomeClassValidator : AbstractValidator<SomeClass>
{
    public SomeClassValidator([depedencies injected here])
    {
        // ... set up validation rules
    }
}

So, is there a better way to register the validators in the bootstrap / composition root, instead of using fluent's validator factory?

P.S. @DotNetJunkie -- would be great if you had a wiki page on this at simpleinjector.codeplex.com.


Solution

  • I think I figured this out myself.

    1.) Register fluent's open generic IValidator<T> interface in the composition root:

    public class SimpleDependencyInjector : IServiceProvider
    {
        public readonly Container Container;
    
        public SimpleDependencyInjector()
        {
            Container = Bootstrap();
        }
    
        internal Container Bootstrap()
        {
            var container = new Container();
    
            // some container registrations
    
            var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
            container.RegisterManyForOpenGeneric(typeof(IValidator<>), assemblies);
    
            // some more registrations
    
            container.Verify();
            return container;
        }
    
        public object GetService(Type serviceType)
        {
            return ((IServiceProvider)Container).GetService(serviceType);
        }
    }
    

    2.) Get rid of the SimpleValidatorFactory class.

    3.) Make the FluentValidatorFactory a non-abstract, concrete class:

    public class FluentValidatorFactory : ValidatorFactoryBase
    {
        private IServiceProvider Injector { get; set; }
    
        public FluentValidatorFactory(IServiceProvider injector)
        {
            Injector = injector;
        }
    
        public override IValidator CreateInstance(Type validatorType)
        {
            return Injector.GetService(validatorType) as IValidator;
        }
    }
    

    4.) Register the FluentValidatorFactory as the validation factory provider in global.asax:

    var injector = new SimpleDependencyInjector();
    FluentValidationModelValidatorProvider.Configure(
        provider =>
        {
            provider.ValidatorFactory = new FluentValidatorFactory(injector);
        }
    );