基于枚举值从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
匹配的消息。
我建议您使用autofac注册一个IMessageHandler工厂类,并在该工厂上实现一个方法,该方法接受您的枚举并返回正确的IMessageHandler实现。
我意识到Autofac的Keyed Services可以帮助我。我想我会采用这种方法。
- 使用属性来声明给定
IMessageHandler
对什么MessageType
感兴趣 - 注册每个键控到
MessageType
的IMessageHandler
- 使用
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");
}
}