Как использовать составной обработчик с вложенным открытым универсальным?

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

Регистр:

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

Вызов события

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, но должен возвращать два обработчика:

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

хотя было бы неплохо:

new ValidateMessageEnvelopeHandler<SecureMessageEnvelope<ItemCreatedEvent>>(
    new SecureMessageEnvelopeHandler<ItemCreatedEvent>(
        new CompositeHandler(
            new MessageLogDecorator<ItemCreatedEvent>(
                new LogService()),
            new MessageLogDecorator<ItemCreatedEvent>(
                new ProjectionService()))));

если я назову "_container.GetInstance(handlerType)" вместо "GetAllInstances", произошла ошибка:

При регистрации открытого типа IMessageHandler произошла ошибка. Не удалось создать регистрацию для типа ValidateMessageEnvelopeHandler>. При регистрации открытого типа IMessageHandler произошла ошибка. Не удалось создать регистрацию для типа SecureMessageEnvelopeHandler. Конструктор типа SecureMessageEnvelopeHandler содержит параметр типа IMessageHandler с именем "innerHandler", который не зарегистрирован. Убедитесь, что IMessageHandler зарегистрирован в контейнере или изменил конструктор SecureMessageEnvelopeHandler.

Когда я регистрирую CompositeHandler(), у меня есть ошибка. (Container.RegisterOpenGeneric(typeof (IMessageHandler <>), typeof (CompositeMessageHandler <>));)

При регистрации открытого типа IMessageHandler произошла ошибка. Не удалось создать регистрацию для типа ValidateMessageEnvelopeHandler>. Несколько наблюдателей события ResolveUnregisteredType регистрируют делегат для одного и того же типа сервиса: IMessageHandler>. Убедитесь, что только один из зарегистрированных обработчиков вызывает метод ResolveUnregisteredType.Register для данного типа службы.

Как я могу это исправить?

Теги:
dependency-injection
simple-injector

1 ответ

1
Лучший ответ

Ваша конфигурация не работает, потому что вы делаете две вещи неправильно:

  1. Вы не зарегистрировали CompositeMessageHandler<T>
  2. Вы вызываете GetAllInstances вместо GetInstance.

Когда вы вызываете GetAllInstances вы получаете только коллекцию обработчиков, которые зарегистрированы с помощью RegisterManyForOpenGeneric, но в вашем примере вы запрашиваете IMessageHandler<ValidateMessageEnvelope<SecureMessageEnvelope<ItemCreatedEvent>>>, но вы никогда не регистрировали IMessageHandler<ValidateMessageEnvelope<T>> как коллекция; ValidateMessageEnvelopeHandler зарегистрирован как регистрация "одного элемента".

Регистрирование для CompositeMessageHandler<T> немного более развито, потому что вы не хотите, чтобы композит возвращался для всех регистраций IMessageHandler<T>. Вы не хотите этого, потому что если сообщение является ValidateMessageEnvelope<T>, вы хотите вернуть ValidateMessageEnvelopeHandler<T> и если это сообщение SecureMessageEnvelope<T>, вы хотите вернуть SecureMessageEnvelopeHandler<T>.

Поэтому вам необходимо добавить следующую регистрацию ПОСЛЕ регистрации для ValidateMessageEnvelope<T> и SecureMessageEnvelopeHandler<T>:

container.RegisterOpenGeneric(
    typeof(IMessageHandler<>), 
    typeof(CompositeMessageHandler<>),
    Lifestyle.Singleton, 
    context => !context.Handled);

Предоставляя context => !context.Handled, вы убедитесь, что CompositeMessageHandler<T> применяется только в том случае, если вначале не была применена другая регистрация. Без этого предиката Simple Injector обнаружит, что две открытые регистрации применяются к одной и той же абстракции, а Simple Injector генерирует исключение.

Вместо вызова GetAllInstances вы должны просто вызвать GetInstance. Например:

var ev = new ItemCreatedEvent().AsSecure("token/1").AsValidatable();
var handlerType = typeof(IMessageHandler<>).MakeGenericType(ev.GetType());

dynamic handler = container.GetInstance(handlerType);

handler.Handle((dynamic)ev);

Обратите внимание, что ваш MessageLogDecorator<T> будет не только применяться к LogService и ProjectionService, но и к классу CompositeMessageHandler<T>. Возможно, это не то, что вы хотите. Возможно, вам захочется - обернуть CompositeMessageHandler<T>, или, возможно, вы хотите обернуть -everything but- CompositeMessageHandler<T>. В последнем случае вы можете изменить регистрацию MessageLogDecorator<T> на следующее:

container.RegisterDecorator(typeof(IMessageHandler<>),
    typeof(MessageLogDecorator<>), context =>
    {
        var type = context.ImplementationType;
        return !type.IsGenericType ||
            type.GetGenericTypeDefinition() != typeof(CompositeMessageHandler<>));
    });

Или вы можете даже упростить его до следующего:

container.RegisterDecorator(typeof(IMessageHandler<>),
    typeof(MessageLogDecorator<>), 
    context => !context.ImplementationType.IsGenericType);

В этом случае декоратор будет только обернут вокруг не общих реализаций.

  • 0
    спасибо за ответ, его работы. Кстати, мне нужен CompositeMessageHandler <>?
  • 0
    @ oguzh4n: составной шаблон не позволяет коду приложения узнать о том, что за этим стоит несколько реализаций. В вашем дизайне, использующем обработчики конвертов, вам это явно необходимо, поскольку в вашем случае вы иногда внедряете один экземпляр, а в других случаях вы вводите композит. И обработчики конвертов, и «посредник исполнителя сообщений» никогда не знают, когда это так (и им не нужно знать).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню