使用Castle Windsor IoC容器在组件注册期间设置Name属性

在我的应用程序中,我有一个名为Message的类。 Message类中存在一个名为MessageType的字符串类型的属性。 MessageType属性用于警告应用程序,以确定Message类实例中将存在哪些数据模式。 Message类派生自名为IMessage的接口。

作为一个例子,假设我们有一个Message类的实例,其MessageType属性的值为“com.business.product.RegisterUser”。

每个MessageType模式都有一个相应的消息处理程序类(MessageHandler),它派生自IMessageHandler接口。 在上面的例子中,会有一个名为RegisterUserMessageHandler的类。 所有消息处理程序类都派生自IMessageHandler。 IMessageHandler定义了一个GetMessageType函数,该函数将返回派生消息实例的MessageType属性。

在使用Castle Windsor IoC容器注册类/组件期间,我想将Name属性设置为Message实例的MessageType属性的值。 我还想使用’通过约定注册组件’方法来注册这些类/组件来完成此任务。

本练习的最终目的是允许我的代码使用Message的MessageType属性调用Resolve方法以获取正确的消息处理程序实例。 例如:

string messageType = "com.business.product.RegisterUser";

IMessageHandler registerUserMessageHandler = _container.Resolve(messageType);

我很感激任何帮助来解决这个问题。 非常感谢。

这是我提出的解决方案,但它确实有一些缺点。 您必须确定您的情况是否可以接受这些缺点。 但是,好处是配置基于约定,并且添加新的IMessageIMessageHandler类型将不需要任何其他Windsor配置。

我使用了Windsor的TypedFactoryFacility和自定义ITypedFactoryComponentSelector 。 通过使用工厂,我删除了您的代码直接调用容器的Resolve方法的需要。 当一个类需要根据消息类型获取IMessageHandler ,它可能只依赖于工厂。 这是工厂:

 public interface IMessageHandlerFactory { IMessageHandler GetMessageHandler(string messageType); } 

由于我正在使用Windsor的TypedFactoryFacility我不需要实现此方法,但是当涉及到调用GetMessageHandler方法时,我将“帮助”Windsor通过定义自定义组件选择器来选择正确的组件:

 public class HandlerTypeSelector : DefaultTypedFactoryComponentSelector { private readonly WindsorContainer container; public HandlerTypeSelector(WindsorContainer container) { this.container = container; } protected override string GetComponentName(MethodInfo method, object[] arguments) { if (method.Name == "GetMessageHandler") { var type = arguments[0].ToString(); var messageHandlers = container.ResolveAll(); var single = messageHandlers.SingleOrDefault(h => h.GetMessageType() == type); if( single != null) return single.GetType().FullName; } return base.GetComponentName(method, arguments); } } 

注册所有这些就像这样简单:

 var container = new WindsorContainer(); container.AddFacility(); container.Register( Component.For().AsFactory(c => c.SelectedWith(new HandlerTypeSelector(container))), AllTypes.FromThisAssembly().BasedOn().WithService.AllInterfaces(), AllTypes.FromThisAssembly().BasedOn().WithService.AllInterfaces() ); 

这是我用来validation的一些测试代码:

 var sampleMessage = new RegisterUserMessage(); var factory = container.Resolve(); var handler = factory.GetMessageHandler(sampleMessage.MessageType); 

我用来测试的类/接口:

 public interface IMessage { string MessageType { get; } } public interface IMessageHandler { string GetMessageType(); } public class RegisterUserMessage : IMessage { public string MessageType { get { return "RegisterUser"; } } } public class RegisterUserMessageHandler : IMessageHandler { public string GetMessageType() { return "RegisterUser"; } } public class RemoveUserMessage : IMessage { public string MessageType { get { return "RemoveUser"; } } } public class RemoveUserMessageHandler : IMessageHandler { public string GetMessageType() { return "RemoveUser"; } } 

现在,缺点是:在组件选择器中,我解析所有 IMessageHandler ,然后根据名称决定使用哪一个(因为我在注册时没有命名消息处理程序,它们将在Windsor中注册,完全限定名称)。 如果您的消息处理程序是轻量级的和/或它们不是很多,或者它们是静态的,那么这可能不是问题。 如果它们是瞬态的和/或具有重要的初始化代码,则每次需要单个代码时解析所有这些代码可能会很昂贵。 在这种情况下,我会废弃整个方法并使用更加配置的东西,但允许直接按名称解析单个消息处理程序。

可能这有点晚了但是,你可以这样做:

 foreach (string messageType in allMessages) { container.Register( Component.For() .ImplementedBy(GetImplementationFor(messageType)) .Named(messageType); ); } Type GetImplementationFor(string messageType) { switch (messageType) { case "com.business.product.RegisterUser": return typeof(RegisterUserService); // more .... default: throw new Exception("Unhandled message type!"); } } // In work: void ProcessMessage(IMessage message) { var handler = this.Container.Resolve(message.MessageType); handler.handle(message); } 

但请确保,因为这种方法您将无法使用任何消息类型名称注册任何其他组件,例如

 container.Register() .ImplementedBy() .Named("com.business.product.RegisterUser"); 

尽管IFoo不是IMessageHandler,但会抛出exception。