2013-07-03 13 views
13

Dati i seguenti:autofac - registrare più decoratori

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"); 
    } 
} 

posso decorare la MoveCustomerCommandHandler con un TransactionCommandHandlerDecorator utilizzando il seguente codice:

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()); 

che sarà in uscita:

TransactionCommandHandlerDecorator - before 
MoveCustomerCommandHandler 
TransactionCommandHandlerDecorator - after 

Come posso decorare anche lo TransactionCommandHandlerDecorator con DeadlockRetryCommandHandlerDecorator, per generare il seguente output

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

risposta

13

Hai solo bisogno di registrare il vostro "TransactionCommandHandlerDecoratored" ICommandHandler come servizio Keyed e l'uso che nuova chiave al momento della registrazione il secondo DeadlockRetryCommandHandlerDecorator:

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

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

E otterrete il seguente output:

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

Domanda aggiuntiva per punti extra: come si registra un decoratore condizionale in Autofac, ad esempio in base a un vincolo di tipo generico? – Steven

+1

@Steven buona domanda, e non ne ho idea :) Questa è la prima volta che ho usato la funzione di decoratore in Autofac per rispondere a questa domanda, quindi devo cercarlo ma immagino che l'impletazione di default non sia così flessibile per fornire condizioni, ma puoi sempre scrivere il tuo 'IRegistrationSource' ... ma immagino ci sia un modo super semplice per farlo in SimpleInjector;) – nemesv

+0

Lo chiedo perché spesso ottengo domande da sviluppatori che vogliono applicare questi pattern utilizzando altri contenitori rispetto a Simple Injector, ma sfortunatamente non sono in grado di fornire loro delle buone risposte - tranne ovviamente il consiglio di usare Simple Injector :-). Ma sì, hai ragione, questa è un'area in cui Simple Injector brilla davvero. – Steven

13

@nemesv ha già risposto a questa domanda, però ho solo pensato che vorrei aggiungere che è possibile aggiungere alcuni metodi di supporto semplici per fare il cablaggio fino sacco di decoratori generici meno doloroso in 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); 
     } 
    } 

Se si incolla questi metodi nel luogo che si sta configurando Autofac, allora si può solo fare questo:

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

e sarà cablare tutti i gestori di comando e aggiungere i decoratori nell'ordine indicato.

+0

Grande pezzo di codice. Grazie mille per la condivisione! –

+0

Può funzionare per Eventhandlers che gestiscono lo stesso evento? –