How to do open generic decorator chaining with unity + UnityAutoRegistration

后端 未结 2 548
遇见更好的自我
遇见更好的自我 2020-12-10 09:08

Went off on an interesting tangent today after reading this article on command handler decoration. I wanted to see if I could implement the pattern using Unity instead of Si

相关标签:
2条回答
  • 2020-12-10 09:31

    I hope I understand the problem correctly and I was curious to try and get this to work and I'm by no means an expert on Unity but I was thinking of a solution that is a little easier to implement and would also be easier to do with a different container. It would seem like the only way to support the open generic as well as the other types is to have 2 separate containers (1 for the open generic) and one for your command handlers, this might not be the best way but it worked with Unity and I'm assuming will also be easier with others.

    So I came up with this:

    I created the containers as follows (you can use your convention approach still im sure for the handler container)

    var container = new UnityContainer();
    
    var container2 = new UnityContainer();
    
    container2.RegisterType(typeof(ICommandHandler<QueryCommand>),
        typeof(QueryCommandHandler));
    container.RegisterInstance("Handlers", container2);
    container.RegisterInstance(container);
    container.RegisterType(typeof(ICommandHandler<>),
        typeof(DecoratedHandler<>));
    

    You see container 2 containing the Handlers as a named instance.

    Then I just created a Generic base decorator class as per the requirement of the pattern:

    public class DecoratorCommandHandler<TCommand>
        : ICommandHandler<TCommand>
    {
        private ICommandHandler<TCommand> inner;
        public DecoratorCommandHandler(
            ICommandHandler<TCommand> inner)
        {
            this.inner = inner;
        }
    
        public virtual void Handle(TCommand command)
        {
             this.inner.Handle(command);
        }
    }
    

    Second I created another Generic Handler that would wrap all the Decorations you want to do for your solution, here you will add decorating for TryCatch/Caching/Transactions or whatever else you want to apply to each command handler:

    public class DecoratedHandler<TCommand>
        : DecoratorCommandHandler<TCommand>
    {
        public DecoratedHandler(UnityContainer container)
            : base(BuildInner(container))
        {
        }
    
        private static ICommandHandler<TCommand> BuildInner(
            UnityContainer container)
        {
             var handlerContainer =
                 container.Resolve<UnityContainer>("Handlers");
             var commandHandler =
                 handlerContainer.Resolve<ICommandHandler<TCommand>>();
    
             return new TryCatchCommandHandler<TCommand>(commandHandler);
        }
    }
    

    You will notice that the first inner resolves the actual command handler as per the one you requested like QueryCommandHandler, UpdateCommandHandler, ExecuteCommandHandler or whatever one dealing with specifics. And then gets wrapped with all the decorators you want common to all of them.

    Then I was able to resolve the correct handler, decorated in the correct way:

    ICommandHandler<QueryCommand> handler =
        container.Resolve<ICommandHandler<QueryCommand>>();
    
    var cmd = new QueryCommand();
    
    handler.Handle(cmd);
    

    Hope this helps

    0 讨论(0)
  • 2020-12-10 09:42

    This would be the equivalent in Unity:

    // Go look in all assemblies and register all implementa-
    // tions of ICommandHandler<T> by their closed interface:
    var container = new UnityContainer();
    
    var handlerRegistrations =
        from assembly in AppDomain.CurrentDomain.GetAssemblies()
        from implementation in assembly.GetExportedTypes()
        where !implementation.IsAbstract
        where !implementation.ContainsGenericParameters
        let services =
            from iface in implementation.GetInterfaces()
            where iface.IsGenericType
            where iface.GetGenericTypeDefinition() == 
                typeof(ICommandHandler<>)
            select iface
        from service in services
        select new { service, implementation };
    
    foreach (var registration in handlerRegistrations)
    {
        container.RegisterType(registration.service, 
            registration.implementation, "Inner1");
    }
    
    // Decorate each returned ICommandHandler<T> object with an
    // TransactionCommandHandlerDecorator<T>.
    container.RegisterType(typeof(ICommandHandler<>), 
        typeof(TransactionCommandHandlerDecorator<>),
        "Inner2",
        InjectionConstructor(new ResolvedParameter(
            typeof(ICommandHandler<>), "Inner1")));
    
    // Decorate each returned ICommandHandler<T> object with an
    // DeadlockRetryCommandHandlerDecorator<T>.
    container.RegisterType(typeof(ICommandHandler<>), 
        typeof(DeadlockRetryCommandHandlerDecorator<>), 
        InjectionConstructor(new ResolvedParameter(
            typeof(ICommandHandler<>), "Inner2")));
    
    0 讨论(0)
提交回复
热议问题