3

使用带有此处描述的命令模式的简单注入器。大多数命令都有实现流利验证的伴随类AbstractValidator<TCommand>,这意味着它们也实现了 FV IValidator<TCommand>。然而,对每个命令都有一个验证器实现并不总是有意义的。

据我所知,命令装饰器实现不能IValidator<TCommand>作为构造函数 arg,除非每个ICommandHandler<TCommand>都有相应的 FV。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(或者应该必须,因为域不是组合根)。

有没有办法告诉 simpleinjector 只CommandHandler<TCommand>用 a 装饰一个实例,FluentValidationCommandDecorator<TCommand>如果有注册的实现IValidator<TCommand>

4

1 回答 1

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

您可以在 wiki 中阅读更多信息:开放泛型类型的注册

于 2012-04-24T20:18:51.610 回答