2013-08-01 12 views
5

のために私たちは、オープン一般的な方法でAutofacによって登録されている一般的なコマンドハンドラがたくさんあります。すべてのハンドルを飾るカップルデコレータがあります。今では、1つのコマンドハンドラだけにデコレータを登録し、他のすべてのコマンドハンドラには影響を与えません。ここで私の試みですが、私は登録権を得ていないようです。ここで登録Autofacデコレータは、唯一の一般的なコマンドハンドラ

は、我々のコードに似ている簡単なテストコードです:

我々はこのような作業コマンドの何百もあります

class NormalCommand : ICommand { } 

// This command handler should not be decorated 
class NormalCommandHandler : ICommandHandler<NormalCommand> 
{ 
    public void Handle(NormalCommand command) { } 
} 

を私はONLY TestCommandHandlerデコレータでTestCommandHandlerDecorator

をラップしたいと思います
class TestCommand : ICommand { } 

// And I would like to put decorator around this handler 
class TestCommandHandler : ICommandHandler<TestCommand> 
{ 
    public void Handle(TestCommand command) { } 
} 

// This decorator should be wrapped only around TestCommandHandler 
class TestCommandHandlerDecorator : ICommandHandler<TestCommand> 
{ 
    private readonly ICommandHandler<TestCommand> decorated; 

    public TestCommandHandlerDecorator(ICommandHandler<TestCommand> decorated) 
    { 
     this.decorated = decorated; 
    } 

    public void Handle(TestCommand command) 
    { 
     // do something 
     decorated.Handle(command); 
     // do something again 
    } 
} 

これは、コンポーネントの登録方法です。

static class AutofacRegistration 
{ 
    public static IContainer RegisterHandlers() 
    { 
     var builder = new ContainerBuilder(); 

     //Register All Command Handlers but not decorators 
     builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(AutofacRegistration))) 
      .Where(t => !t.Name.EndsWith("Decorator")) 
      .AsClosedTypesOf(typeof(ICommandHandler<>)) 
      .InstancePerLifetimeScope(); 

     // and here is the battle! 
     builder.RegisterType<TestCommandHandler>() 
       .Named<ICommandHandler<TestCommand>>("TestHandler") 
       .InstancePerLifetimeScope(); 

     // this does not seem to wrap the decorator 
     builder.RegisterDecorator<ICommandHandler<TestCommand>>(
      (c, inner) => new TestCommandHandlerDecorator(inner), 
      fromKey: "TestHandler") 
       .Named<ICommandHandler<TestCommand>>("TestHandler1") 
       .InstancePerLifetimeScope(); 

     return builder.Build(); 
    } 
} 

そして、これは私がコマンドハンドラ/デコレータの正しいインスタンスを取得することを確認しようとする方法である:

class AutofacRegistrationTests 
{ 
    [Test] 
    public void ResolveNormalCommand() 
    { 
     var container = AutofacRegistration.RegisterHandlers(); 

     var result = container.Resolve<ICommandHandler<NormalCommand>>(); 

     // this resolves correctly 
     Assert.IsInstanceOf<NormalCommandHandler>(result); // pass 
    } 

    [Test] 
    public void TestCommand_Resolves_AsDecorated() 
    { 
     var container = AutofacRegistration.RegisterHandlers(); 

     var result = container.Resolve<ICommandHandler<TestCommand>>(); 

     // and this resolves to TestCommandHandler, not decorated! 
     Assert.IsInstanceOf<TestCommandHandlerDecorator>(result); // FAILS! 
    } 
} 

コメントが言うように、デコレータはデコレータ登録が無視され、適用される取得されていません。

このデコレータを登録する方法を任意のIDE ??私は間違って何をしていますか?

+0

私は別のDIコンテナを使用してソリューションを提供することができます、またはあなたはオートファックに夢中になっていますか? – Steven

+0

私は今Autofacに夢中になっていますが、Structure MapやWindsorで例を挙げることができれば、私もそれを調べることに興味があります。教育目的のため。 – trailmax

答えて

3

キーボードに対して十分な時間を私の頭を平手た後、私は私の問題への解決策のいくつかの種類を持っている:ここで

static class AutofacRegistration 
{ 
    public static IContainer RegisterHandlers() 
    { 
     var builder = new ContainerBuilder(); 

     builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(AutofacRegistration))) 
      .AsClosedTypesOf(typeof(ICommandHandler<>)) 
      .InstancePerLifetimeScope(); 

     builder.RegisterType<TestCommandHandler>() 
       .Named<ICommandHandler<TestCommand>>("TestHandler") 
       .InstancePerLifetimeScope(); 

     // this works! 
     builder.Register(c => new TestCommandHandlerDecorator(c.ResolveNamed<ICommandHandler<TestCommand>>("TestHandler"))) 
       .As<ICommandHandler<TestCommand>>() 
       .InstancePerLifetimeScope(); 

     return builder.Build(); 
    } 
} 

私はAutofacのデコレータ機能を使用して、手動でデコレータをラップではありませんよ。だからデコレータの依存関係の数が増えたら、必要なすべての依存関係を解決するためにコンテナを更新する必要があります。

あなたがよりよい解決策を知っている場合は、私に知らせてください!

1

私は城ウィンザーかのStructureMap上の任意の例を与えることができない、と私の経験ではAutofacとシンプルインジェクターよりも何か他のものを使用したオープンジェネリックデコレータを適用するのは非常に困難です。条件付きでオープンジェネリックデコレータを適用する場合(具体的なシナリオ)、AFAIK Simple Injectorは、このための降下をサポートする唯一のDIコンテナです。シンプルなインジェクターで

、次のようにすべてのあなたのコマンドハンドラを登録し、次のよう

container.RegisterManyForOpenGeneric(
    typeof(ICommandHandler<>), 
    typeof(ICommandHandler<>).Assembly); 

デコレータを登録することができます。

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(CommandHandlerDecorator1<>)); 

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(TestCommandHandlerDecorator)); 

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(CommandHandlerDecorator2<>)); 

デコレータを意味し、それらが登録されている順に追加されます上記の場合、CommandHandlerDecorator2<T>TestCommandHandlerDecoratorをラップし、CommandHandlerDecorator1<T>を包み込み、具体的なコマンドハンドラをラップします。 TestCommandHandlerDecoratorは1つの特定のためのものですICommandHandler<T>それはそのようなタイプの周りにのみラップされます。あなたのケースでは、以前の登録をした後に完了します。

しかし、あなたの場合は、実際には単純なケースです。デコレータは特定の登録に適用されている場合、あなたがコントロールすることができRegisterDecoratorに述語を供給することにより

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(SomeDecorator<>), c => 
     c.ServiceType.GetGenericArguments()[0] == typeof(TestCommand)); 

:シンプルなインジェクターは、このような条件付き述語またはジェネリック型の制約に基づいてデコレータを適用するなどはるかに興味深いシナリオをサポートしています。

別のオプションは、ジェネリック型制約をデコレータに適用することです。シンプルなインジェクタは、ジェネリック型の制約を処理することができる:

// This decorator should be wrapped only around TestCommandHandler 
class TestCommandHandlerDecorator<T> : ICommandHandler<T> 
    where T : TestCommand // GENERIC TYPE CONSTRAINT 
{ 
    // ... 
} 

あなたはTestCommandから派生したコマンドを処理する任意のコマンドハンドラを持っている場合に便利ですが、多くの場合、あなたはコマンドが1を実装するか、複数のインターフェースとデコレータがあることがわかりますそれらのインタフェースの1つでコマンドを処理するコマンドハンドラに適用されます。次のように

しかし、どちらの方法では、デコレータは単純に登録することができます:私は最後にあなたがすべてのコンテナでこの作業を取得できると思いますが

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(TestCommandHandlerDecorator<>)); 

は、ほとんどのコンテナは、これが本当に実現するために複雑になります。これは、Simple Injectorが優れている場所です。 @のtrailmaxの答えに手動登録を避けるため

+1

ありがとう、スティーブン、それはかなり面白いです。私はSimpleInjectorにチャンスを与えて、次のプロジェクトで試してみる必要があると思います。私は本当にジェネリック型の制約の2番目のオプションが好きです。これは、Autofacで実装を開始したときに気にしていたことです。 – trailmax

3

次の拡張メソッドを定義することができます

public static class ContainerBuilderExtensions 
{ 
    public static void RegisterDecorator<TService, TDecorater, TInterface>(this ContainerBuilder builder, 
     Action<IRegistrationBuilder<TService, ConcreteReflectionActivatorData, SingleRegistrationStyle>> serviceAction, 
     Action<IRegistrationBuilder<TDecorater, ConcreteReflectionActivatorData, SingleRegistrationStyle>> decoratorAction) 
    { 
     IRegistrationBuilder<TService, ConcreteReflectionActivatorData, SingleRegistrationStyle> serviceBuilder = builder 
      .RegisterType<TService>() 
      .Named<TInterface>(typeof (TService).Name); 

     serviceAction(serviceBuilder); 

     IRegistrationBuilder<TDecorater, ConcreteReflectionActivatorData, SingleRegistrationStyle> decoratorBuilder = 
      builder.RegisterType<TDecorater>() 
       .WithParameter(
        (p, c) => p.ParameterType == typeof (TInterface), 
        (p, c) => c.ResolveNamed<TInterface>(typeof (TService).Name)) 
       .As<TInterface>(); 

     decoratorAction(decoratorBuilder); 
    } 
} 

をそしてそうのようにこれを使用する:

 builder.RegisterDecorator<TestCommandHandler, TestCommandHandlerDecorator, ICommandHandler<TestCommand>>(
      s => s.InstancePerLifetimeScope(), 
      d => d.InstancePerLifetimeScope()); 
+0

これはかなりクールだ。私はそれを行ってあげるよ。全体的な質問は、実際のアプリケーションがないパズルのようなものでした。 – trailmax

関連する問題