3

単純なインジェクタを使用with the command pattern described here。ほとんどのコマンドには、流暢な検証のAbstractValidator<TCommand>を実装するコンパニオンクラスがあります。つまり、FV IValidator<TCommand>も実装しています。しかし、すべてのコマンドにバリデーターの実装を持たせるのは必ずしも意味がありません。いくつかの型に実装がない場合、これはRegisterDecoratorの正しい方法ですか?

私が知る限り、コマンドデコレータの実装では、ICommandHandler<TCommand>に対応するFVがない限り、コンストラクタargとして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()以上を実行ユニットテストは、2番目の呼び出しに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>のためにそこに登録されている場合のみ、FluentValidationCommandDecorator<TCommand>CommandHandler<TCommand>インスタンスを飾るためにsimpleinjectorを伝える方法はありますか?

答えて

2

不足しているタイプをデフォルトの実装にマップするには、未登録のタイプ解決が必要です。

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

は今、あなたは空/デフォルトの実装でIValidator<T>を実装NullValidator<T>定義する必要があります。言い換えれば、あなたはRegisterOpenGenericメソッドを使用する必要があります。

特定の(登録されていない)IValidator<T>が要求されるたびにこれを行うと、新しいインスタンスNullValidator<T>が返されます。これらの型は明示的に登録されているため、RegisterManyForOpenGenericを使用して登録された型はオーバーライドされません。

NullValidator<T>実装はスレッドセーフ(通常は空の実装の場合となります)、あなたはシングルトンとしてそれらを登録することにより、建設を最適化することができます:あなたはウィキで詳しい情報を読むことができます

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

Registration of open generic types

+0

登録されていないタイプの解像度です。 'NullValidator <>'はsimpleinjectorで同じlibに属しているようですが、これでデコレータを移動することができます。私のNullValidatorはFluentValidatorの 'AbstractValidator 'を継承しています - コンストラクタ、フィールド、何もありません。スレッドセーフであることから、単一として登録しています。 – danludwig

+0

もう一つの副作用は、 'IValidator 'の実装を持つコマンドは、実際の実装で1回、NullValidatorで2回装飾されているということです。これは期待されていますか? – danludwig

+0

'NullValidator'をデコレータにする理由はわかりません。あなたが何をしているのか分かりません。現在のコードと設定で質問を更新できますか? – Steven

関連する問題