3

ここで説明するコマンドパターンで単純なインジェクターを使用します。ほとんどのコマンドには、流暢な検証を実装するコンパニオンクラスがあります。AbstractValidator<TCommand>つまり、FVも実装しIValidator<TCommand>ます。ただし、すべてのコマンドにバリデーターを実装することが常に意味があるとは限りません。

私が知る限り、コマンドデコレータの実装は、すべてに対応するFVがIValidator<TCommand>ない限り、コンストラクタ引数として使用することはできません。。私は次のことを試しました:ICommandHandler<TCommand>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;
    });

1回実行される単体テストContainer.Verify()に合格します。Container.Verify()複数回実行される単体テストInvalidOperationExceptionは、2回目の呼び出しで失敗します。

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

このクラスがSimpleInjectorに依存する必要がない場合は、ドメインプロジェクトに移動できます。ドメインはすでにFluentValidation.netに依存しているため、ドメインの有効性を単体テストできます。このデコレータはドメインに属していると思いますが、このデコレータもその単体テストプロジェクトも、simpleinjectorに依存していません(ドメインはコンポジションルートではないため、依存している必要があります)。

実装が登録されてCommandHandler<TCommand>いる場合にのみインスタンスを装飾するようにsimpleinjectorに指示する方法はありますか?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<>));

ウィキで詳細を読むことができます:オープンジェネリック型の登録

于 2012-04-24T20:18:51.610 に答える