2015-11-03 2 views
2

Я пытаюсь автоматизировать проверку моделей моего представления, я знаю, что могу просто добавить атрибут для указания моей проверки, но есть возможность настроить фабрику для автоматизации всего этого, я посмотрел: this answer и придумал это, используя простой инжектор 3.1:SimpleInjector и FluentValidationFactory

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

Однако переменная реализация всегда пустой, я также пытался RegisterCollection, но до сих пор такая же проблема, кажется как простой инжектор не знает, как решить IValidator когда валидатор наследует от AbstractValidator (Это класс, который реализует IValidator)

+0

Почему вы регистрируете валидаторы в конструкторе завода? Я удивлен, что этот код работает. Я не могу думать ни о какой причине, почему вы этого хотели. Вы должны поместить этот код в свой корневой каталог приложения. –

+0

Можете ли вы показать минимальный воспроизводимый пример? Как зарегистрирована CustomValidatorFactory и как она называется? Что фактически передается параметру fsctory? Является ли завод поставляемым с типом (Person) или typeof (IValidator )? – Steven

+0

@ Ric.Net, этот CustomValidatorFactory можно считать частью корня композиции, поэтому регистрация здесь кажется прекрасной. – Steven

ответ

5

Зарегистрируйте Fluent Validation Factory Простая Injector так:

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

, а затем в ваш состав 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.

UPDATE

Вы можете зарегистрировать пустой валидатор для объекта, который не имеет в валидатор, как:

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

Спасибо за редактирование, @Steven - был какой-то copy/waste :-) – janhartmann

+1

это решение разрешило большинство моих проблем, единственная проблема заключалась в том, что он выдавал ошибку, когда была модель представления без валидатора, поэтому я просто заменил эту строку: 'return (IValidator) _container.GetInstance (validator); ' с: ' return ((IServiceProvider) _container) .GetService (validator) как идентификатор; ' Я думаю, что единственное различие заключается в том, что метод GetService просто вернет null whi ch подходит для этого сценария. –

+0

@ danif430 см. Мое обновление – janhartmann

1

Я хотел бы поделиться своим опытом с интегрированием Simple Инжектор с FluentValidation здесь.

Моей первая попытка похожа на то, что @janhartmann сделал, реализовать конкретный ValidatorFactoryBase, который принимает простой инжектор 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 s, Simple Injector выбрасывает ActivationException и сбой приложения.

Моя вторая попытка, конечно, поставить примерки поймать блок вокруг GetInstance:

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

Но тогда я не удовлетворен тем, что примерочных улов будет, очевидно, замедлять разрешение валидаторов так Я оглядываюсь вокруг, чтобы найти API, который возвращает Container null вместо того, чтобы бросать исключение. Это оказывается возможным, потому что Container implements IServiceProvider explicitly и IServiceProvider вернут null, если тип не зарегистрирован.

Моя третья и последняя попытка, и так как этот валидатор завод больше не зависит от простого 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 в качестве дополнительного выгода.

Смежные вопросы