4

この素晴らしい記事https://cuttingedge.it/blogs/steven/pivot/entry.php?id=9の影響を強く受けて、アプリケーションに CQRS アプローチを実装しました。コマンドとハンドラーのコードは、記事と同じように設定されており、その部分はうまく機能しています。コマンドの検証を処理するデコレータ クラスを実装しようとすると、私の問題が発生します。シンプルなコマンド処理インターフェースは次のようになります。

public interface ICommand
{
}

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

それから私が持っている検証デコレータのために:

public class ValidationCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> where TCommand : CommandBase
{
    private readonly ICommandHandler<TCommand> _decoratedCommandHandler;
    private readonly ICommandValidator<TCommand> _commandValidator;

    public ValidationCommandHandlerDecorator(ICommandHandler<TCommand> decoratedCommandHandler, ICommandValidator<TCommand> commandValidator)
    {
        _decoratedCommandHandler = decoratedCommandHandler;
        _commandValidator = commandValidator;
    }

    public void Handle(TCommand command)
    {
        if (_commandValidator != null)
        {
            var validationResult = _commandValidator.Validate(command);

            if (validationResult != null)
            {
                command.Success = false;
                command.Errors = validationResult;
                return;
            }
        }

        _decoratedCommandHandler.Handle(command);
        command.Success = true;
    }
}    

バリデーターを定義するためにインターフェースを使用します。

public interface ICommandValidator<TCommand>
{
    IEnumerable<string> Validate(TCommand command);
}

AndCommandBaseは、コマンドの成功または失敗と、失敗した場合に発生したエラーを格納できる単純な基本クラスです。例外をスローする代わりに、この方法を使用することをお勧めします。すべてのコマンドは、この基本クラスを継承します。

public abstract class CommandBase : ICommand
{
    public bool Success { get; set; }
    public IEnumerable<string> Errors { get; set; }
}

そして、これはすべて、構造マップ レジストリ内の IoC コンテナーに接続されています。

public class CommandRegistry : Registry
{
    public CommandRegistry()
    {
        Scan(s =>
        {
            s.AssemblyContainingType<CommandBase>();
            s.ConnectImplementationsToTypesClosing(typeof(ICommandHandler<>));
            s.ConnectImplementationsToTypesClosing(typeof(ICommandValidator<>));
            s.WithDefaultConventions();

            For(typeof(ICommandHandler<>)).DecorateAllWith(typeof(ValidationCommandHandlerDecorator<>));
        });
    }
}

ここで、ICommandHandler ごとにそのデコレーターを登録するので、バリデーターを必要とせず、バリデーターを定義しないコマンドがある場合ICommandValidator<TCommand> _commandValidator、クラスのプライベート フィールドはValidationCommandHandlerDecorator<TCommand>もちろん存在せず、常に次の構造マップ エラーをスローします。

「デフォルトのインスタンスが登録されておらず、タイプ 'ICommandValidator' に対して自動的に決定できません ICommandValidator に指定された構成がありません」

クラス内のコンテナに依存したり、コマンドを処理ValidationCommandHandlerDecoratorするためのインターフェイスを作成したりする必要なく、存在しない場合に何らかのタイプのデフォルトバリデータを使用するように、構造マップで get の構築方法を定義する方法はありますか?IValidateableCommandHandler<TCommand>バリデーター?

ありがとうございました。

4

1 に答える 1

4

後で誰かがこれに遭遇した場合に備えて、私が思いついた解決策は、DefaultCommandValidator クラスを Null オブジェクト パターン クラスとして追加することでした。

public class DefaultCommandValidator<TCommand> : ICommandValidator<TCommand> where TCommand : CommandBase
{
    public IEnumerable<string> Validate(TCommand command)
    {
        return Enumerable.Empty<string>();
    }
}

次に、構造マップ レジストリに次の行を追加します。

For(typeof(ICommandValidator<>)).Use(typeof(DefaultCommandValidator<>));

の具体的な実装が見つからない場合にのみ、この構造マップ構文が実際にデフォルトのインスタンスのみを使用することを知りませんでしICommandValidator<TCommand>た。ここで、バリデータがない場合は追加せず、DefaultCommandValidator<TCommand>インスタンスを使用して空の/成功したバリデーションを返します。

于 2015-08-10T15:09:39.753 に答える