根据枚举值从IoC容器中解析

时间:2011-05-28 22:24:08

标签: c# ioc-container autofac protobuf-net

问题

我有一个带有MessageType字段的protobuf消息定义,这是一个枚举。鉴于传入的protobuf消息,我想基于IMessageHandler从IoC容器中解析一些MessageType。问题有两个:如何在编写MessageType时表达IMessageHandler约束,以及如何仅从IoC容器中解析所需的处理程序?

我正在使用Autofac,但我有兴趣听取任何容器的解决方案。

我的想法:

为了表达约束,我看到两个选项:属性或属性(不能使用泛型,因为它是枚举值)。我喜欢该属性,因为它使得在不指定约束的情况下编写IMessageHandler是不可能的,但缺点是必须先实例化它才能看到属性值。

在我目前的代码中,我正在使用属性方法。我解决了所有IMessageHandler并手动进行过滤,但似乎应该有更好的方法。并不是说我太担心性能,只是闻起来我正在解决那些不习惯的实例。

更新

为了让它更清晰一点,这就是我现在正在做的事情

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并调用TargetType与传入消息的MessageType匹配的消息。

2 个答案:

答案 0 :(得分:2)

我建议您使用autofac注册一个IMessageHandler-Factory类,并在该工厂上实现一个获取枚举的方法,并返回正确的IMessageHandler实现。

答案 1 :(得分:2)

我意识到Autofac's Keyed Services可以帮助我。我想我会采用这种方法。

  1. 使用属性声明给定MessageType感兴趣的IMessageHandler
  2. 注册每个键入IMessageHandler
  3. MessageType
  4. 使用ResolveKeyed仅获取我感兴趣的IMessageHandler
  5. 好消息是,如果有人忘记使用该属性,我们可以在构建容器时捕获它。这是一个完整的例子。欢迎任何建议!

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