Разрешить из контейнера IoC на основе значения перечисления - PullRequest
2 голосов
/ 29 мая 2011

Задача

У меня есть определение сообщения protobuf с полем MessageType, которое является enum. Учитывая входящее сообщение protobuf, я хотел бы разрешить некоторые IMessageHandler с из контейнера IoC на основе MessageType. Проблема двоякая: как выразить ограничение MessageType при написании IMessageHandler и как разрешить только нужные обработчики из контейнера IoC?

Я использую Autofac, но мне интересны слуховые решения для любого контейнера.

Мои мысли:

Для выражения ограничения я вижу две опции: свойство или атрибут (нельзя использовать обобщенные значения, потому что это значение перечисления). Мне нравится свойство, потому что оно делает невозможным запись IMessageHandler без указания ограничения, но недостатком является то, что его необходимо создать, прежде чем вы сможете увидеть значение свойства.

В моем текущем коде я использую подход с использованием свойств. Я разрешаю все IMessageHandler s и выполняю фильтрацию вручную, но, похоже, должен быть лучший способ. Не то, чтобы я слишком беспокоился о производительности, это просто отчасти пахнет тем, что я решаю случаи, которые не привыкают.

Обновление

Чтобы было немного яснее, вот что я делаю сейчас

public interface IMessageHandler
{
    MessageType TargetType { get; }

    void Handle(IMessage message);
}

public class SomeHandler : IMessageHandler
{
    public MessageType TargetType
    {
        get { return MessageType.Type1;  }
    }

    public void Handle(IMessage message)
    {
        // implementation
    }
}

Поэтому, когда я получаю сообщение protobuf, я разрешаю все IMessageHandler s и вызываю те, чей TargetType соответствует MessageType входящего сообщения.

Ответы [ 2 ]

2 голосов
/ 29 мая 2011

Я понял, что Службы Keyed Services Autofac могут мне помочь.Я думаю, что я собираюсь пойти с этим подходом.

  1. Используйте атрибут, чтобы объявить, что MessageType данный IMessageHandler интересует.
  2. Зарегистрируйте каждый IMessageHandlerMessageType 1012 *
  3. Используйте ResolveKeyed, чтобы получить только IMessageHandler s, которые мне интересны.

Хорошо, если кто-то забудет использовать атрибут, мы можем поймать его при сборке контейнера.Вот полный пример.Любые предложения приветствуются!

class Program
{
    static IContainer container;

    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterAssemblyTypes(typeof(Program).Assembly)
            .AssignableTo<IMessageHandler>()
            .Keyed<IMessageHandler>(t => GetMessageType(t));

        container = builder.Build();

        InvokeHandlers(MessageType.Type1);
        InvokeHandlers(MessageType.Type2);

        Console.ReadKey();
    }

    static MessageType GetMessageType(Type type)
    {
        var att = type.GetCustomAttributes(true).OfType<MessageHandlerAttribute>().FirstOrDefault();
        if (att == null)
        {
            throw new Exception("Somone forgot to put the MessageHandlerAttribute on an IMessageHandler!");
        }

        return att.MessageType;
    }

    static void InvokeHandlers(MessageType type)
    {
        using (var lifetime = container.BeginLifetimeScope())
        {
            // I'm impressed that Autofac knows what I mean here!
            var handlers = lifetime.ResolveKeyed<IEnumerable<IMessageHandler>>(type);
            foreach (var handler in handlers)
            {
                handler.Handle();
            }
        }
    }
}

public enum MessageType
{
    Type1,
    Type2,
}

public interface IMessageHandler
{
    void Handle();
}

public class MessageHandlerAttribute : Attribute
{
    public MessageHandlerAttribute(MessageType messageType)
    {
        MessageType = messageType;
    }

    public MessageType MessageType { get; private set; }
}

[MessageHandler(MessageType.Type1)]
public class Handler1 : IMessageHandler
{
    public void Handle()
    {
        Console.WriteLine("A handler for Type1");
    }
}

[MessageHandler(MessageType.Type1)]
public class Handler2 : IMessageHandler
{
    public void Handle()
    {
        Console.WriteLine("Another handler for Type1");
    }
}

[MessageHandler(MessageType.Type2)]
public class Handler3 : IMessageHandler
{
    public void Handle()
    {
        Console.WriteLine("A handler for Type2");
    }
}
2 голосов
/ 29 мая 2011

Я предлагаю вам зарегистрировать один класс IMessageHandler-Factory с помощью autofac и реализовать метод для этой фабрики, который принимает ваше перечисление и возвращает правильную реализацию IMessageHandler.

...