2013-07-03 155 views
13

考慮以下幾點:Autofac - 註冊多個裝飾

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

public class MoveCustomerCommand 
{ 

} 

public class MoveCustomerCommandHandler : ICommandHandler<MoveCustomerCommand> 
{ 
    public void Handle(MoveCustomerCommand command) 
    { 
     Console.WriteLine("MoveCustomerCommandHandler"); 
    } 
} 

public class TransactionCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> 
{ 
    private readonly ICommandHandler<TCommand> _decorated; 

    public TransactionCommandHandlerDecorator(ICommandHandler<TCommand> decorated) 
    { 
     _decorated = decorated; 
    } 

    public void Handle(TCommand command) 
    { 
     Console.WriteLine("TransactionCommandHandlerDecorator - before"); 
     _decorated.Handle(command); 
     Console.WriteLine("TransactionCommandHandlerDecorator - after"); 
    } 
} 

public class DeadlockRetryCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> 
{ 
    private readonly ICommandHandler<TCommand> _decorated; 

    public DeadlockRetryCommandHandlerDecorator(ICommandHandler<TCommand> decorated) 
    { 
     _decorated = decorated; 
    } 

    public void Handle(TCommand command) 
    { 
     Console.WriteLine("DeadlockRetryCommandHandlerDecorator - before"); 
     _decorated.Handle(command); 
     Console.WriteLine("DeadlockRetryCommandHandlerDecorator - after"); 
    } 
} 

我可以使用下面的代碼裝飾MoveCustomerCommandHandlerTransactionCommandHandlerDecorator

var builder = new ContainerBuilder(); 

builder.RegisterAssemblyTypes(typeof(MoveCustomerCommandHandler).Assembly) 
    .As(type => type.GetInterfaces() 
    .Where(interfaceType => interfaceType.IsClosedTypeOf(typeof(ICommandHandler<>))) 
    .Select(interfaceType => new KeyedService("commandHandler", interfaceType))); 

builder.RegisterGenericDecorator(
     typeof(TransactionCommandHandlerDecorator<>), 
     typeof(ICommandHandler<>), 
     fromKey: "commandHandler"); 

var container = builder.Build(); 

var commandHandler = container.Resolve<ICommandHandler<MoveCustomerCommand>>(); 
commandHandler.Handle(new MoveCustomerCommand()); 

將輸出:

TransactionCommandHandlerDecorator - before 
MoveCustomerCommandHandler 
TransactionCommandHandlerDecorator - after 

我怎樣才能用裝飾,生成以下輸出

DeadlockRetryCommandHandlerDecorator- before 
TransactionCommandHandlerDecorator - before 
MoveCustomerCommandHandler 
TransactionCommandHandlerDecorator - after 
DeadlockRetryCommandHandlerDecorator- after 

回答

13

你只需要註冊你的第二個DeadlockRetryCommandHandlerDecorator時註冊「TransactionCommandHandlerDecoratored」 ICommandHandlerKeyed服務,並使用新的密鑰:

builder.RegisterGenericDecorator(
     typeof(TransactionCommandHandlerDecorator<>), 
     typeof(ICommandHandler<>), 
     fromKey: "commandHandler") 
     .Keyed("decorated", typeof(ICommandHandler<>)); 

builder.RegisterGenericDecorator(
     typeof(DeadlockRetryCommandHandlerDecorator<>), 
     typeof(ICommandHandler<>), 
     fromKey: "decorated"); 

,你會得到以下輸出:

DeadlockRetryCommandHandlerDecorator - before 
TransactionCommandHandlerDecorator - before 
MoveCustomerCommandHandler 
TransactionCommandHandlerDecorator - after 
DeadlockRetryCommandHandlerDecorator - after 
+0

附加問題加分:你如何在Autofac中註冊一個有條件的裝飾器,例如基於泛型約束? – Steven

+1

@Steven很好的問題,我不知道:)這是我第一次使用Autofac中的裝飾器功能來回答這個問題,所以我必須查看它,但我想默認的實現並不是那麼靈活提供條件,但你總是可以編寫你自己的'IRegistrationSource' ...但我想在SimpleInjector中有很簡單的方法來做到這一點;) – nemesv

+0

我這樣問,因爲我經常從想要應用這些模式的開發者那裏得到問題使用其他容器,而不是簡單的注射器,但不幸的是我無法給他們提供很好的答案 - 除了使用簡單注射器的建議:-)。但是,你是對的,這是一個簡單的噴油器真正發光的區域。 – Steven

13

@nemesv已經回答了這個問題,不過,我只是覺得應該補充一點,你可以添加一些簡單的輔助方法,使接線,很多普通裝飾的Autofac痛苦少:

private static void RegisterHandlers(
     ContainerBuilder builder, 
     Type handlerType, 
     params Type[] decorators) 
    { 
     RegisterHandlers(builder, handlerType); 

     for (int i = 0; i < decorators.Length; i++) 
     { 
      RegisterGenericDecorator(
       builder, 
       decorators[i], 
       handlerType, 
       i == 0 ? handlerType : decorators[i - 1], 
       i != decorators.Length - 1); 
     } 
    } 

    private static void RegisterHandlers(ContainerBuilder builder, Type handlerType) 
    { 
     builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) 
      .As(t => t.GetInterfaces() 
        .Where(v => v.IsClosedTypeOf(handlerType)) 
        .Select(v => new KeyedService(handlerType.Name, v))) 
      .InstancePerRequest(); 
    } 

    private static void RegisterGenericDecorator(
     ContainerBuilder builder, 
     Type decoratorType, 
     Type decoratedServiceType, 
     Type fromKeyType, 
     bool hasKey) 
    { 
     var result = builder.RegisterGenericDecorator(
      decoratorType, 
      decoratedServiceType, 
      fromKeyType.Name); 

     if (hasKey) 
     { 
      result.Keyed(decoratorType.Name, decoratedServiceType); 
     } 
    } 

如果粘貼這些方法到地方,你正在配置Autofac,那麼你可以這樣做:

RegisterHandlers(
     builder, 
     typeof(ICommandHandler<>), 
     typeof(TransactionCommandHandlerDecorator<>), 
     typeof(ValidationCommandHandlerDecorator<>)); 

它會連接你所有的命令處理程序,並按給定的順序添加裝飾器。

+0

偉大的一段代碼。非常感謝分享! –

+0

這可以用於Eventhandlers處理同一事件嗎? –