2013-08-01 27 views
5

我们有许多通用命令处理程序,它们是由Autofac以开放式通用方式注册的。我们有装饰所有手柄的几个装饰器。现在我只需要为一个命令处理程序注册一个装饰器,而不会影响所有其他命令处理程序。这是我的尝试,但我似乎没有获得注册权。只为一个通用命令处理程序注册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! 
    } 
} 

正如评论说,是没有得到应用的装饰,装饰注册将被忽略。

任何ides如何注册这个装饰器?我究竟做错了什么?

+0

我可以提供使用其他的DI容器的解决方案,还是你迷上了Autofac? – 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

我无法举出Castle Windsor或StructureMap上的任何示例,并且根据我的经验,使用Autofac和Simple Injector以外的任何其他应用程序很难应用开放通用装饰器。当涉及到有条件地应用开放的通用装饰器(您的特定场景)时,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只适用于一个特定的ICommandHandler<T>,因此仅包含这些类型。因此,就你的情况而言,在完成之前的注册后你就完成了。

但你的情况实际上是一个简单的情况。简单的喷油器支持更多有趣的场景,如基于谓词或一个泛型类型约束条件将装饰:

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

通过提供谓词到RegisterDecorator如果装饰应用到某些注册就可以控制。

另一种选择是将通用类型约束应用于装饰器。简单的喷油器能够处理泛型类型的限制:

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

,当你有一个处理从TestCommand获得命令的任何命令处理程序,但往往你会看到命令实现一个或多个接口和装饰都是这是有用的应用于使用其中一个接口处理命令的命令处理程序。

但无论哪种方式,装饰可以简单地注册如下:

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

虽然我认为那到底你可以在每个集装箱这个工作,大多数容器将使这个很复杂来实现。这是简单注射器的优点。

+1

谢谢你,史蒂文,这很有趣。我想我必须给SimpleInjector一个机会,并在我的下一个项目上尝试它。我真的很喜欢泛型类型约束的第二个选项 - 这是我在开始在Autofac中实现时想到的。 – trailmax

3

为了避免@ trailmax的答案手动注册,您可以定义以下扩展方法:

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

相关问题