public interface IMessage { }
public interface ICommand : IMessage { }
public interface IEvent : IMessage { }
public interface IMessageHandler<T> {
void Handle(T message);
}
public class ItemCreatedEvent : IEvent {
public string Name { get; set; }
}
public class ValidateMessageEnvelope<TMessage> {
public TMessage Message { get; set; }
public ValidateMessageEnvelope(TMessage message){
Message = message;
}
}
public class ValidateMessageEnvelopeHandler<TMessage>
: IMessageHandler<ValidateMessageEnvelope<TMessage>> {
private readonly IMessageHandler<TMessage> _innerHandler;
public ValidateMessageEnvelopeHandler(IMessageHandler<TMessage> innerHandler){
_innerHandler = innerHandler;
}
public void Handle(ValidateMessageEnvelope<TMessage> message){
_innerHandler.Handle(message.Message);
}
}
public class SecureMessageEnvelope<TMessage> {
public TMessage Message { get; set; }
public string UserToken { get; set; }
public SecureMessageEnvelope(TMessage message, string userToken){
Message = message;
UserToken = userToken;
}
}
public class SecureMessageEnvelopeHandler<TMessage>
: IMessageHandler<SecureMessageEnvelope<TMessage>>
{
private readonly IMessageHandler<TMessage> _innerHandler;
public SecureMessageEnvelopeHandler(IMessageHandler<TMessage> innerHandler){
_innerHandler = innerHandler;
}
public void Handle(SecureMessageEnvelope<TMessage> message){
_innerHandler.Handle(message.Message);
}
}
public class MessageLogDecorator<TMessage> : IMessageHandler<TMessage>
where TMessage : IEvent {
private readonly IMessageHandler<TMessage> _messageHandler;
public MessageLogDecorator(IMessageHandler<TMessage> messageHandler) {
_messageHandler = messageHandler;
}
public void Handle(TMessage message){
Console.WriteLine("Event Log: {0}",JsonConvert.SerializeObject(message));
_messageHandler.Handle(message);
}
}
public class CompositeMessageHandler<TMessage> : IMessageHandler<TMessage> {
private readonly IEnumerable<IMessageHandler<TMessage>> _handlers;
public CompositeMessageHandler(IEnumerable<IMessageHandler<TMessage>> handlers){
_handlers = handlers;
}
public void Handle(TMessage message) {
foreach (var messageHandler in _handlers) {
messageHandler.Handle(message);
}
}
}
public class LogService :IMessageHandler<ItemCreatedEvent> {
public void Handle(ItemCreatedEvent message) {}
}
public class ProjectionService: IMessageHandler<ItemCreatedEvent> {
public void Handle(ItemCreatedEvent message) { }
}
public static class Extensions{
public static SecureMessageEnvelope<TMessage> AsSecure<TMessage>(
this TMessage message, string userToken){
return new SecureMessageEnvelope<TMessage>(message, userToken);
}
public static ValidateMessageEnvelope<TMessage> AsValidatable<TMessage>(
this TMessage message){
return new ValidateMessageEnvelope<TMessage>(message);
}
}
Register:
Container.RegisterManyForOpenGeneric(typeof (IMessageHandler<>),
Container.RegisterAll,
Assembly.GetExecutingAssembly());
// handle all ValidateMessageEnvelope<TMessage> messages
Container.RegisterOpenGeneric(typeof(IMessageHandler<>),
typeof(ValidateMessageEnvelopeHandler<>));
// handle all SecureMessageEnvelope<TMessage> messages
Container.RegisterOpenGeneric(typeof(IMessageHandler<>),
typeof(SecureMessageEnvelopeHandler<>));
// handle all IEvent messages
Container.RegisterDecorator(typeof(IMessageHandler<>),
typeof(MessageLogDecorator<>));
Event Call
var ev = new ItemCreatedEvent().AsSecure("token/1").AsValidatable();
var handlerType = typeof(IMessageHandler<>).MakeGenericType(ev.GetType());
foreach (dynamic handler in _container.GetAllInstances(handlerType)){
handler.Handle((dynamic)ev);
}
return empty but must return two handler:
handler[0] =
new ValidateMessageEnvelopeHandler<SecureMessageEnvelope<ItemCreatedEvent>>(
new SecureMessageEnvelopeHandler<ItemCreatedEvent>(
new MessageLogDecorator<ItemCreatedEvent>(
new LogService())));
handler[1] =
new ValidateMessageEnvelopeHandler<SecureMessageEnvelope<ItemCreatedEvent>>(
new SecureMessageEnvelopeHandler<ItemCreatedEvent>(
new MessageLogDecorator<ItemCreatedEvent>(
new ProjectionService())));
although it would be nice as follows:
new ValidateMessageEnvelopeHandler<SecureMessageEnvelope<ItemCreatedEvent>>(
new SecureMessageEnvelopeHandler<ItemCreatedEvent>(
new CompositeHandler(
new MessageLogDecorator<ItemCreatedEvent>(
new LogService()),
new MessageLogDecorator<ItemCreatedEvent>(
new ProjectionService()))));
if I call "_container.GetInstance(handlerType)" instead of "GetAllInstances" an error occured:
There was an error in the registration of open generic type IMessageHandler. Failed to build a registration for type ValidateMessageEnvelopeHandler>. There was an error in the registration of open generic type IMessageHandler. Failed to build a registration for type SecureMessageEnvelopeHandler. The constructor of the type SecureMessageEnvelopeHandler contains the parameter of type IMessageHandler with name 'innerHandler' that is not registered. Please ensure IMessageHandler is registered in the container, or change the constructor of SecureMessageEnvelopeHandler.
When I register CompositeHandler () I have error. ( Container.RegisterOpenGeneric(typeof(IMessageHandler<>), typeof(CompositeMessageHandler<>));)
There was an error in the registration of open generic type IMessageHandler. Failed to build a registration for type ValidateMessageEnvelopeHandler>. Multiple observers of the ResolveUnregisteredType event are registering a delegate for the same service type: IMessageHandler>. Make sure only one of the registered handlers calls the ResolveUnregisteredType.Register method for a given service type.
How can I fix this?
Your configuration doesn't work because you are doing two things wrong:
CompositeMessageHandler<T>
GetAllInstances
instead ofGetInstance
.When you call
GetAllInstances
you only get a collection of handlers that are registered using theRegisterManyForOpenGeneric
, but in your example you are requesting anIMessageHandler<ValidateMessageEnvelope<SecureMessageEnvelope<ItemCreatedEvent>>>
, but you never registered anIMessageHandler<ValidateMessageEnvelope<T>>
as collection; theValidateMessageEnvelopeHandler
is registered as 'single item' registration.The regisration for the
CompositeMessageHandler<T>
is a bit more evolved, because you don't want the composite to be returned for allIMessageHandler<T>
registrations. You don't want this, because if the message is aValidateMessageEnvelope<T>
, you want to return theValidateMessageEnvelopeHandler<T>
and if the message is aSecureMessageEnvelope<T>
, you want to return theSecureMessageEnvelopeHandler<T>
.So you need to add the following registration AFTER the registrations for the
ValidateMessageEnvelope<T>
andSecureMessageEnvelopeHandler<T>
:By supplying a
context => !context.Handled
, you make sure that theCompositeMessageHandler<T>
only gets applied if no other registration was applied first. Without this predicate, Simple Injector will detect that two open-generic registrations are applied to the same abstraction and Simple Injector will throw an exception.Instead of calling
GetAllInstances
, you should simply callGetInstance
. For instance:Do note though that your
MessageLogDecorator<T>
will not only get applied to theLogService
andProjectionService
classes, but also to theCompositeMessageHandler<T>
class. This might not be what you want. You might want to -only- wrap theCompositeMessageHandler<T>
, or perhaps you want to wrap -everything but- theCompositeMessageHandler<T>
. In the last case, you can change theMessageLogDecorator<T>
's registration to the following:Or you might even be able to simplify it to the following:
In which case the decorator will only get wrapped around non-generic implementations.