SimpleInjector和FluentValidationFactory

时间:2015-11-03 22:42:38

标签: c# fluentvalidation simple-injector

我正在尝试自动验证我的视图模型,我知道我可以添加一个属性来指定我的验证,但是有一个选项来设置工厂来自动化所有这些,我看了一下:{{3并使用简单的注入器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;
        }
    }

然后视图模型可以是

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);
    }
}

然而实现变量总是为null,我也尝试过RegisterCollection但仍然有相同的问题,看起来简单的注入器不知道如何在验证器继承自AbstractValidator时解析IValidator(这是实现的类IValidator)

2 个答案:

答案 0 :(得分:6)

在Simple Injector中注册Fluent Validation Factory,如下所示:

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);
    }
}

然后在你的Composition Root中为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;
    }
);

FluentValidationModelValidatorProvider可在FluentValidation.MVC集成包中找到。记得为你正在运行的MVC版本获取一个。

<强>更新

您可以为没有验证器的对象注册一个空验证器,如:

/// <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);

答案 1 :(得分:2)

我想分享我在这里将Simple Injector与FluentValidation集成的经验。

我的第一次尝试与@janhartmann所做的类似,实现了一个具体的ValidatorFactoryBase,它将Simple Injector的Container作为依赖:

public class SimpleInjectorValidatorFactory : ValidatorFactoryBase
{
    private readonly Container _container;

    public SimpleInjectorValidatorFactory(Container container)
        => _container = container;

    public override IValidator CreateInstance(Type validatorType)
        => (IValidator)_container.GetInstance(validatorType);
}

public static class CompositionRoot
{
    public static void RegisterDependencies()
    {
        var container = new Container();
        FluentValidationModelValidatorProvider.Configure(
            provider => provider.ValidatorFactory = 
                new SimpleInjectorValidatorFactory(container));
    }
}

然而,这是有效的,我在ASP.NET MVC项目的上下文中使用此工厂,并且对于依赖于没有为IValidator注册的模型绑定魔术的视图模型,Simple Injector抛出ActivationException并崩溃应用程序。

我的第二次尝试当然是在GetInstance周围放置一个try-catch块:

public class SimpleInjectorValidatorFactory : ValidatorFactoryBase
{
    private readonly Container _container;

    public SimpleInjectorValidatorFactory(Container container)
        => _container = container;

    public override IValidator CreateInstance(Type validatorType)
    {
        try
        {
            object validator = _container.GetInstance(validatorType);
            return (IValidator)validator;
        }
        catch (ActivationException)
        {
            // FluentValidation will handle null properly
            return null;
        }
    }
}

但是后来我对try-catch显然会降低验证器的分辨率这一事实感到不满意,所以我环顾四周找到使Container返回null而不是抛出异常的API。事实证明这是可能的,因为如果类型未注册,Container implements IServiceProvider explicitlyIServiceProvider将返回null

我的第三次也是最后一次尝试,由于此验证工厂不再依赖于Simple Injector,我已将其重命名为ServiceProviderValidatorFactory

public class ServiceProviderValidatorFactory : ValidatorFactoryBase
{
    private readonly IServiceProvider _serviceProvider;

    public ServiceProviderValidatorFactory(IServiceProvider serviceProvider)
        => _serviceProvider = serviceProvider;

    public override IValidator CreateInstance(Type validatorType)
        => (IValidator)_serviceProvider.GetService(validatorType);
}

public static class CompositionRoot
{
    public static void RegisterDependencies()
    {
        var container = new Container();
        FluentValidationModelValidatorProvider.Configure(
            provider => provider.ValidatorFactory = 
                new ServiceProviderValidatorFactory(container));
    }
}

这可以使验证器工厂与Simple Injector完全解耦,这是一个额外的好处。