问题
So, I have a message bus that instantiates message handlers through Ninject. I'd like to decorate my handlers with cross cutting concerns such as logging, transaction management, etc.
I setup my bindings like so:
kernel.Bind<IMessageHandler<int>>().To<IntHandlerOne>()
.WhenInjectedInto(typeof(HandlerDecorator<>));
kernel.Bind(typeof(IMessageHandler<>)).To(typeof(HandlerDecorator<>));
Which works fantastically whenever I have a single handler of a specific message type. However, when I have more than one handler defined:
kernel.Bind<IMessageHandler<int>>().To<IntHandlerOne>()
.WhenInjectedInto(typeof(HandlerDecorator<>));
kernel.Bind<IMessageHandler<int>>().To<IntHandlerTwo>()
.WhenInjectedInto(typeof(HandlerDecorator<>));
kernel.Bind(typeof(IMessageHandler<>)).To(typeof(HandlerDecorator<>));
Ninject will find and inject the decorator to the message bus, and then attempt unsuccessfully to inject both handlers into the decorator constructor.
public HandlerDecorator(IMessageHandler<T> handler)
You may be thinking, why don't I just modify my decorator to accept the list of handlers? I thought about this, but that defeats the purpose of the handler. I want to be able to easily chain multiple decorators together transparently. Each instance of IMessageHandler<T>
should get an entirely new chain of handlers.
I've published an example test library on GitHub that should illustrate what I'm talking about here.
Is there any way to do this in Ninject?
回答1:
Use
kernel.Bind<IMessageHandler<int>>().To<IntHandlerOne>().WhenParentNamed("One");
kernel.Bind<IMessageHandler<int>>().To<IntHandlerTwo>().WhenParentNamed("Two");
kernel.Bind(typeof(IMessageHandler<>)).To(typeof(HandlerDecorator<>)).Named("One");
kernel.Bind(typeof(IMessageHandler<>)).To(typeof(HandlerDecorator<>)).Named("Two");
Also be aware that most of the Bus Frameworks have some way to do decorations for message handlers. May have a look there first.
回答2:
You should wrap those handlers in a composite:
public class CompositeMessageHandler<T> : IMessageHandler<T>
{
private readonly IEnumerable<IMessageHandler<T>> handlers;
CompositeMessageHandler(IEnumerable<IMessageHandler<T>> handlers)
{
this.handlers = handlers;
}
public void Handle(T message)
{
foreach (var handler in this.handlers)
{
handler.Handle(message);
}
}
}
This composite can again be injected into your decorator. Or perhaps you should do it the other way around: Wrap each handler with a decorator and wrap those into the composite.
I'm not sure how to register this with Ninject though.
来源:https://stackoverflow.com/questions/13278958/how-to-decorate-interfaces-bound-to-more-than-one-concrete-type-with-ninject