3

使用简单的喷油器with the command pattern described here。大多数命令都有配套类,可以实现流畅验证的AbstractValidator<TCommand>,这意味着它们也可以实现FV IValidator<TCommand>。然而,对于每个命令都有一个验证器实现并不总是有意义的。当某些类型没有实现时,这是RegisterDecorator的正确方法吗?

据我所知,除非每个ICommandHandler<TCommand>都有相应的FV,否则命令装饰器实现不能将IValidator<TCommand>作为构造函数arg。 IValidator<TCommand>。我试过以下内容:

public class FluentValidationCommandDecorator<TCommand> 
    : IHandleCommands<TCommand> 
{ 
    public FluentValidationCommandDecorator(IHandleCommands<TCommand> decorated 
     , IValidator<TCommand> validator 
    ) 
    { 
     _decorated = decorated; 
     _validator = validator; 
    } 
    ... 
} 
... 
container.RegisterManyForOpenGeneric(typeof(IValidator<>), assemblies); 
container.RegisterDecorator(typeof(IHandleCommands<>), 
    typeof(FluentValidationCommandDecorator<>), 
    context => 
    { 
     var validatorType = 
      typeof (IValidator<>).MakeGenericType(
       context.ServiceType.GetGenericArguments()); 
     if (container.GetRegistration(validatorType) == null) 
      return false; 
     return true; 
    }); 

单元测试运行Container.Verify()一次,通过。运行Container.Verify()一次以上的单元测试,从InvalidOperationException失败的第二个调用:

The configuration is invalid. Creating the instance for type 
IValidator<SomeCommandThatHasNoValidatorImplementation> failed. Object reference 
not set to an instance of an object. 

下工作,通过采取Container作为参数:

public class FluentValidationCommandDecorator<TCommand> 
    : IHandleCommands<TCommand> 
{ 
    private readonly IHandleCommands<TCommand> _decorated; 
    private readonly Container _container; 

    public FluentValidationCommandDecorator(Container container 
     , IHandleCommands<TCommand> decorated 
    ) 
    { 
     _container = container; 
     _decorated = decorated; 
    } 

    public void Handle(TCommand command) 
    { 
     IValidator<TCommand> validator = null; 
     if (_container.GetRegistration(typeof(IValidator<TCommand>)) != null) 
      validator = _container.GetInstance<IValidator<TCommand>>(); 

     if (validator != null) validator.ValidateAndThrow(command); 

     _decorated.Handle(command); 
    } 
} 
... 
container.RegisterManyForOpenGeneric(typeof(IValidator<>), assemblies); 
container.RegisterDecorator(typeof(IHandleCommands<>), 
    typeof(FluentValidationCommandDecorator<>)); 

如果这个类没有必须依赖Simple Injector,我可以将它移动到域项目中。该域名已经依赖FluentValidation.net,因此域名有效性可以进行单元测试。我认为这个装饰器属于域中,但它既不是它的单元测试项目也不依赖简单注入器(或者不得不这样做,因为域不是组合根)。

有没有办法告诉simpleinjector如果有一个实现注册IValidator<TCommand>只装修CommandHandler<TCommand>实例与FluentValidationCommandDecorator<TCommand>

回答

2

您需要的是未注册的类型解析,将缺失的类型映射到默认实现。或者换句话说,你需要使用RegisterOpenGeneric方法:

container.RegisterOpenGeneric(typeof(IValidator<>), 
    typeof(NullValidator<>)); 

现在你需要定义一个NullValidator<T>与空/默认实现实现IValidator<T>

每当请求某个(未注册的)IValidator<T>时,都会返回NullValidator<T>的新实例。它不会覆盖使用RegisterManyForOpenGeneric注册的类型,因为这些类型已被明确注册。

NullValidator<T>实现是线程安全的(这通常是一个空的实现的情况下),你可以注册为单身优化建设:

container.RegisterSingleOpenGeneric(typeof(IValidator<>), 
    typeof(NullValidator<>)); 

您可以在维基阅读更多信息:Registration of open generic types

+0

未注册的类型解析,得到它。看起来'NullValidator <>'确实属于与simpleinjector相同的库,但是这允许我将装饰器移出。我的NullValidator只是继承了Fluentvalidation的'AbstractValidator ' - 没有构造函数,字段,什么都没有 - 并且从我能告诉的是线程安全的,所以我将它注册为单个。 – danludwig 2012-04-24 21:20:19

+0

这样做的另一个副作用似乎是有两个'IValidator '实现的命令被装饰了两次 - 一次是真正的实现,一次是NullValidator。这是预期的吗? – danludwig 2012-04-24 21:27:18

+0

我不知道为什么你应该让'NullValidator'成为一个装饰器。我不确定你在做什么。你能用你当前的代码和配置更新你的问题吗? – Steven 2012-04-25 07:14:54

相关问题