2011-01-19 10 views
8

に対するジェネリック型を登録するのStructureMap:私は、次ているすべての可能な具体的な実装

:私は、コンテナ内の次のタイプを取得するように、私は> ICommandHandler <を登録するためにスキャンを使用したい

public interface ICommand { } 
public class AddUser : ICommand 
{ 
    public string Name { get; set; } 
    public string Password { get; set; } 
} 

public interface ICommandHandler<T> : IHandler<T> where T : ICommand 
{ 
    void Execute(T command); 
} 

public class AddUserHandler : ICommandHandler<AddUser> 
{ 
    public void Execute(AddUser command) 
    { 
     Console.WriteLine("{0}: User added: {1}", GetType().Name, command.Name); 
    } 
} 

public class AuditTrailHandler : ICommandHandler<ICommand> 
{ 
    public void Execute(ICommand command) 
    { 
     Console.WriteLine("{0}: Have seen a command of type {1}", GetType().Name, command.GetType().Name); 
    } 
} 

コンクリート型のコンクリート型の

  • ICommandHandler<AddUser>AddUserHandler
  • ICommandHandler<AddUser>​​

私はIRegistrationConventionの実装でこれを試しましたが、ある時点では動作していましたが、私がやったやり方について頭を上げることはできません。

目標はそうのような特定のICommandの実装のためのいくつかのハンドラを実行できるようにすることです:

// A method in CommandDispatcher 
public void SendCommand<T>(T command) where T : ICommand { 
    var commandHandlers = container.GetAllInstances<ICommandHandler<T>>(); 
    foreach (var commandHandler in commandHandlers) { 
     commandHandler.Execute(command);  
    } 
} 

私はすべてのためにそれらを登録するので、必要性、AuditTrailHandler<ICommand>はのICommandのすべての具体的な実装を実行したいですICommandのタイプ。

二次的な目的は、コンテナではなく私のCommandDispatcherにICommandHandlers<ICommand>のコレクションを注入できるかどうかです。しかし、今の構造では不可能だと思います。何か考えがあるなら間違っていることを証明してください。

EDITは - 私は私の一般的なインタフェースが実装非ジェネリックインターフェイスを追加し、私も抽象CommandHandler<T>を追加しましたので、私はすべてで(オブジェクト)メソッドをCanHandleを実装または実行する必要はありません

を解決しました私のハンドラー。

この

は、作業構造である:

public interface ICommandHandler 
{ 
    void Execute(object command); 
    bool CanHandle(ICommand command); 
} 

public interface ICommandHandler<T> : ICommandHandler, IHandler<T> where T : ICommand 
{ 
    void Execute(T command); 
} 

public abstract class AbstractCommandHandler<T> : ICommandHandler<T> where T : ICommand 
{ 
    public abstract void Execute(T command); 
    public void Execute(object command) 
    { 
     Execute((T)command); 
    } 

    public virtual bool CanHandle(ICommand command) 
    { 
     return command is T; 
    } 
} 

そして、これは、もともとのStructureMapの質問があったため、ここではこれを追加するためのスキャンです:

Scan(x => 
     { 
      x.AssemblyContainingType<MyConcreteHandler>(); 
      x.IncludeNamespaceContainingType<MyConcreteHandler>(); 
      x.AddAllTypesOf<ICommandHandler>(); 
      x.WithDefaultConventions(); 
     }); 

これは中のIEnumerableを注入する私にできるようになり私のCommandDispatcherは次のように実行します。

public void SendCommand<T>(T command) where T : ICommand 
    { 
     var commandHandlersThatCanHandle = commandHandlers.Where(c => c.CanHandle(command)); 
     foreach (var commandHandler in commandHandlersThatCanHandle) 
     { 
      commandHandler.Execute(command);  
     } 
    } 

これで私はComを実行できますAddUser(AddUserHandlerのような)をサポートするMandHandlerですが、(AuditTrailHandlerのような)ICommandをサポートするハンドラも実行できます。

これは甘いです!

答えて

4

コマンドディスパッチャにすべてのコマンドハンドラを注入するには、ICommandHandler<T>が由来する新しい非汎用のICommandHandlerインターフェイスを作成します。これはオブジェクトをとるExecuteメソッドを持っています。 StructureMapが自動的に移入されますされ、IEnumerable<ICommandHandler>にコンストラクタ依存性を持っているあなたのコマンドディスパッチャを有効にします

public class AddUserHandler : ICommandHandler<AddUser> 
{ 
    public void Execute(object command) 
    { 
     Execute((AddUser)command); 
    } 
    public void Execute(AddUser command) 
    { 
     Console.WriteLine("{0}: User added: {1}", GetType().Name, command.Name); 
    } 
} 

この:欠点は、あなたのコマンドハンドラのそれぞれは、入力されたオーバーロードを呼び出すために、そのメソッドを実装しなければならないことです。

SendCommandには、適切なハンドラのセットを取得する2つの方法があります。タイプに基づいて単純なフィルタ:

commandHandlers.OfType<ICommandHandler<T>> 

またはICommandHandlerインタフェースにCanHandle(object command)を追加します。

commandHandlers.Where(x => x.CanHandle(command)) 

第二のアプローチは、より多くのコードが必要ですが、あなたはハンドラをアタッチできるようにすることで、もう少し柔軟性を提供します単にタイプするだけではありません。​​はいつもtrueCanHandleから返すことで、最初の問題を簡単に解決できます。

+0

これは素晴らしいです! 私は以前にCanHandleメソッドを持っていましたが、StructureMapの魔法が私のためにできると思っていたので、それを削除しました。 私はもう少し行って抽象CommandHandler を作成し、ジェネリックインターフェイスとコンクリートクラスの間に置いて、派生したすべてのクラスでそれを実装せずにCanHandleメソッドの恩恵を受けることができました。 恐ろしい、ありがとう! –

関連する問題