基于枚举值从IoC容器解析

本文关键字:IoC 于枚举 枚举 | 更新日期: 2023-09-27 17:58:04

问题

我有一个protobuf消息定义,其中有一个MessageType字段,它是一个枚举。给定一个传入的protobuf消息,我想从基于MessageType的IoC容器中解析一些IMessageHandler。问题有两个:在编写IMessageHandler时,如何表达MessageType约束,以及如何仅从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匹配的消息。

基于枚举值从IoC容器解析

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

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

  1. 使用属性来声明给定IMessageHandler对什么MessageType感兴趣
  2. 注册每个键控到MessageTypeIMessageHandler
  3. 使用ResolveKeyed只获取我感兴趣的IMessageHandler

好的是,如果有人忘记使用该属性,我们可以在构建容器时捕获它。这是一个完整的例子。欢迎提出任何建议!

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