StructureMap 2.6 でオープン ジェネリック型のインターセプトを介してコマンド ハンドラーをロギング デコレーターでラップするデコレーター規則を使用しているプロジェクトがあります。しかし、アップグレードを完了するために、StructureMap 3 に同等の機能を実装する最善の方法を見つけるのに苦労しています。
以下は StructureMap 2.6 のコードです。まず、私の IoC クラスでは、コマンド ハンドラーを解決するためのスキャン ポリシーを設定しています。
scan.ConnectImplementationsToTypesClosing(typeof(ICommandHandler<>));
次に、IoC のスキャン規則に追加されたデコレータ規則があり、デコレータ インターセプトを接続します。
public class CommandLoggingDecoratorConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
var interfaceTypes = type.GetInterfaces();
foreach (var interfaceType in interfaceTypes)
{
if (interfaceType.IsGenericType
&& interfaceType.GetGenericTypeDefinition() == typeof(ICommandHandler<>))
{
var arguments = interfaceType.GetGenericArguments();
var closedType = typeof(CommandHandlerLoggingDecorator<>)
.MakeGenericType(arguments);
registry.For(interfaceType)
.EnrichWith((c, p) => Activator.CreateInstance(
closedType,
p,
c.GetInstance<IMessageLoggingHelper>(),
c.GetInstance<ILog>()));
}
}
}
}
次に、特定のコマンドをコマンド ハンドラーにマップし、ロギング デコレーター (コマンド ハンドラーをラップしている) で Execute メソッドを呼び出すコマンド バスがあります。これは、デコレーター内のコマンドで Execute メソッドを呼び出します。
public class CommandBus : ICommandBus
{
public static IContainer Container;
public void Execute(ICommand command)
{
var handlerType = typeof (ICommandHandler<>)
.MakeGenericType(command.GetType());
dynamic handler = Container
.GetAllInstances(handlerType)
.Cast<dynamic>()
.Single();
handler.Execute((dynamic) command);
}
}
デコレーター規則をインターセプター ポリシーに置き換え、インターセプター ポリシーを IoC クラスに追加することで、StructureMap 3 でこれを機能させることができました。
インターセプター ポリシーは次のとおりです。
public class CommandLoggingDecoratorPolicy : IInterceptorPolicy
{
public string Description { get; private set; }
public IEnumerable<IInterceptor> DetermineInterceptors(Type pluginType, Instance instance)
{
if (pluginType == typeof (ICommandHandler<>))
yield return new DecoratorInterceptor(
typeof(ICommandHandler<>),
typeof(CommandHandlerLoggingDecorator<>));
}
そして、これを IoC のインターセプター ポリシーに追加するコードは次のとおりです。
x.Policies.Interceptors(new CommandLoggingDecoratorPolicy());
ただし、Container.GetInstance (私の CommandBus 内) を呼び出すと、コマンド ログ デコレータではなく、一致するコマンド ハンドラーの実装が返されます。Container.GetAllInstances を呼び出すと、実装 (1 つ目) とデコレータ (2 つ目) の両方が返されます。
したがって、現時点でこれを機能させる唯一の方法は、Container.GetAllInstances から返された 2 番目の項目を明示的に選択するか、結果をフィルター処理してリフレクションを使用してデコレーターを選択することです。次に例を示します。
public class CommandBus : ICommandBus
{
public static IContainer Container;
public void Execute(ICommand command)
{
var handlerType = typeof (ICommandHandler<>)
.MakeGenericType(command.GetType());
var handlers = Container
.GetAllInstances(handlerType)
.Cast<dynamic>();
var handler = handlers.ToList()[1];
handler.Execute((dynamic) command);
}
}
ただし、これはかなり醜い解決策のようです。明らかに私が見逃しているものがあるに違いありません。まず、デコレータ インターセプト ポリシーを明示的に追加したのに、Container.GetInstance がデコレータではなく実装を返すのはなぜですか? 第二に、これを完全に行うためのより良い方法はありますか?
どんなアイデアや提案も大歓迎です!