Decorating with Generics

43 views
Skip to first unread message

Edward May

unread,
Jun 7, 2018, 4:21:54 PM6/7/18
to structuremap-users
I have an interface with a generic that inherits from a non-generic base marker interface.

public interface ICommandHandler<in T> : ICommandHandler
{
    IServices Handle(T options);
}

public interface ICommandHandler { }

There will be many implementations and use ctor injection to pass them all in.  This works fine when using the base marker interface

protected AbstractArgumentParser(ICommandHandler[] commandHandlers)
{
    _commandHandlers = commandHandlers;
}

I can get the handler I want by using some reflection:
protected ICommandHandler<T> GetHandler<T>()
{
    ICommandHandler<T> handler = null;
    foreach (var e in _commandHandlers)
    {
        Type genericType = e.GetType().GetInterface("ICommandHandler`1").GetGenericArguments()[0];
        if (genericType == typeof(T))
        {
            handler = (ICommandHandler<T>)e;
            break;
        }
     }

     return handler;
}


Now, I'd like to decorate the generic form of the handlers

class CommandHandlerLoggingDecoratorPolicy : DecoratorPolicy
{
    public CommandHandlerLoggingDecoratorPolicy() : base(typeof(ICommandHandler<>), typeof(CommandHandlerLoggingDecorator<>), type => 
}


Of course, this doesn't work, since the non-generic form is being injected.  AbstractArgumentParser is in a NuGet library and will have unknown number of implementations, so I can't pass in each generic form in the ctor and would violate open/closed anyway. 

What's a good way to do this?  

Jeremy Miller

unread,
Jun 8, 2018, 7:03:35 AM6/8/18
to structure...@googlegroups.com
If you haven’t already, check out the last section in this page of the SM docs: http://structuremap.github.io/generics/

Your usage would be a bit more complicated because of the variable number of generic parameters, but still similar conceptually.


--
You received this message because you are subscribed to the Google Groups "structuremap-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to structuremap-us...@googlegroups.com.
To post to this group, send email to structure...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/structuremap-users/24386606-f0b0-4201-bb15-6e8495546e3e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Edward May

unread,
Aug 2, 2018, 2:36:18 PM8/2/18
to structuremap-users
Finally getting back to actually implementing this.  I'm still confused how I should go about this.  In the section you referred me to it shows use of the ActivatorInterceptor.  Not sure how that would work with decorator pattern.  I tried to just copy in the code from DecoratorPolicy and DecoratorInterceptor and modify it for my use.  Had the DecoratorInterceptor.Return return ICommandHandler, but it complains when it calls Instance.ApplyAllPolicies: 

Specified argument was out of the range of valid values.Parameter name: ReturnedType LoggingCommandHandlerDecorator<T> cannot be cast to the Interceptor Accepts type ICommandHandler

Even though I think that should totally work since it does ultimately derive from that class..  In any event I feel I'm missing something here, probably overkill to bring in two classes from framework.

Edward May

unread,
Aug 2, 2018, 5:06:18 PM8/2/18
to structuremap-users
Finally figure out a way.  Was really trying to do this decorator style, but then had to add a "AddHandler" to my factory.  Intercept that and then add decorated handlers.

class LoggingCommandHandlerDecoratorPolicy : IInterceptorPolicy
    {
        public string Description => "Added logging decorated handler to factory";

        public IEnumerable<IInterceptor> DetermineInterceptors(Type pluginType, Instance instance)
        {
            if (instance.ReturnedType ==  typeof(CommandHandlerFactory))
            {
                Expression<Action<IContext, CommandHandlerFactory>> register = (c, o) => AddExecutor(c, o);
                yield return new ActivatorInterceptor<CommandHandlerFactory>(register);
            }
        }

        private void AddExecutor(IContext context, CommandHandlerFactory commandHandlerFactory)
        {
            ICommandHandler[] handlersToDecorate = context.GetInstance<ICommandHandler[]>();

            foreach (var handler in handlersToDecorate)
            {
                Type genericType = handler.GetType().GetInterface("IICommandHandler`1").GetGenericArguments()[0];

                ILogger logger = context.GetInstance<ILogger>();

                Type loggerHandlerType = typeof(LoggingCommandHandlerDecorator<>);
                Type[] typeArgs = { genericType };
                Type makeme = loggerHandlerType.MakeGenericType(typeArgs);
                object o = Activator.CreateInstance(makeme, handler, logger);

                commandHandlerFactory.AddExecutor(o as ICommandHandler);
            }
        }
    }


Reply all
Reply to author
Forward
0 new messages