I am using SimpleInjector with MediatR and hooking up my INotifications and INotificationHandlers<INotification> with attributes on my implementation classes - this is so we can map message bus messages (aka notifications) to notification handlers:主机应用收到来自总线的消息邮件被解析为Notification POCO容器用于查找通知处理程序通知将分派给每个处理程序传统上,将所有INotificationHandler<>注册到Container.GetTypesToRegister()和Container.Collection.Register(),并且一切都会很好,因为容器会针对该特定的INotification返回所有处理程序.Traditionally one would register all INotificationHandler<> with Container.GetTypesToRegister() and Container.Collection.Register(), and all would be well in that all handlers would be returned by the container for that specific INotification.在这种情况下,我希望获得一个实现要调用的特定INotificationHandler<SpecificEvent>的特定具体实例.我们决定使用什么属性:In this case, I wish to get a specific concrete instances that implements the specific INotificationHandler<SpecificEvent> to be called. We decide what is called with attributes:[MessageBusSubscription("v1/alerts/fire"]class FirstClass : INotificationHandler<Aggregate>{ public Task Handle(Aggregate notification, CancellationToken cancellationToken) { throw new NotImplementedException(); }}[MessageBusSubscription("v1/alerts/fire"]class SecondClass : INotificationHandler<Aggregate>{ public Task Handle(Aggregate notification, CancellationToken cancellationToken) { throw new NotImplementedException(); }}[MessageBusSubscription("v1/alerts/leak"]class OtherClass : INotificationHandler<Aggregate>{ public Task Handle(Aggregate notification, CancellationToken cancellationToken) { throw new NotImplementedException(); }}我知道我可以获取并转换为接口(此转换是安全的,因为在注册引导基础结构期间将强制检查类型是否实现了接口):I know I could get and convert to the interface (this cast would be safe as during the registration bootstrap infrastructure would enforce checking that the type implements the interface):Container Bootstrap(){ var container = new Container(); // we cannot register the below with Container.Collection.Register as we wont be finding the registration // by INotificationHandler<INotification> but rather by the specific class (we could auto-wire by reflection) container.Register<FirstClass>(); container.Register<OtherClass>(); // other bootstrap code, including registration of all other INotificationHandler<> that do not have // MessageBusSubscriptionAttribute in the cases where we want to publish to all implementations // that are not scoped to a specific topic subscription or message type return container;}IEnumerable<INotificationHandler<TNotification>>> GetConcreteTypeThatImplementsNotificationHandlers<TNotification>(string topic) where TNotification : INotification{ // TopicRegistrations consists of the following: // { "v1/alerts/fire", [] { typeof(FirstClass), typeof(SecondClass) } // { "v1/alerts/leak", [] { typeof(OtherClass) } // based on notification type and topic string, get concrete implementation var concreteTypes = TopicRegistrations.GetTypeForTopic(topic); foreach(var type in concreteTypes) { var instance = Container.GetInstance(type); yield return (INotificationHandler<TNotification>>)instance; }}然后我可以将上述内容称为该特定通知类型的INotificationHandler,并且仅基于属性元数据的特定具体实现.I could then call the above as if it were a INotificationHandler for that specific notification type and only for specific concrete implementation(s) based on attribute metadata.但是,当使用上述代码方法时,我如何确保仍将任何装饰器(如日志记录或错误处理程序等)仍应用于获取的NotificationHandler?However, how would I ensure that any decorators (like logging or error handlers etc) are still applied to the NotificationHandler fetched when using the above code approach?最终,我正在寻找类似于以下内容的Container函数,该函数将仅针对提供的具体类型的 返回INotificationHandler,并应用装饰器(如果有):Ultimately I am looking for a Container function similar to the below that would return the INotificationHandler for only the provided concrete type, and have decorators (if any) applied:var notficationHandlerType = typeof(INotificationHandler<>).MakeGenericType(notificationFromBus.GetType());Container.GetRegistrationOnlyForConcreateType(notficationHandlerType, concreteType);可以做些什么来使这种清洁器更清洁,或者消除任何可能引起爆炸或散发气味的风险?Anything that could be done to make this cleaner or missing any risks that could blow up or code smells? 更新感谢史蒂文(Steven)出色的代码,感谢您启发我们更好地编写代码.我对其进行了些许修改,以使其采用一种类型,并为该类型注册了特定的处理程序:Thanks to Steven for his wonderful code, thanks for inspiring us to code better. I have adapted it slightly so that it takes a type, and the specific handlers to register for that type:public class TestNotification : INotification { }public class DifferentTestNotification : INotification { }public class OtherClass : INotificationHandler<TestNotification>, INotificationHandler<DifferentTestNotification>{ public Task Handle(TestNotification notification, CancellationToken cancellationToken) { throw new NotImplementedException(); } public Task Handle(DifferentTestNotification notification, CancellationToken cancellationToken) { throw new NotImplementedException(); }}/* repeat the exact same for AmazingClass and AllStars giving 3 identical classes for which we can selectively register different handlers later */并传递一个我想注册的处理程序字典:And pass a dictionary of the handlers that I wish to register:registrationTypes = new Dictionary<Type, Type[]>(){ { typeof(OtherClass), new [] { typeof(INotificationHandler<>).MakeGenericType(typeof(TestNotification)) } }, { typeof(AmazingClass), new [] { typeof(INotificationHandler<>).MakeGenericType(typeof(DifferentTestNotification)) } }, { typeof(AllStars), new [] { typeof(INotificationHandler<>).MakeGenericType(typeof(TestNotification)), typeof(INotificationHandler<>).MakeGenericType(typeof(DifferentTestNotification)) } }};进入以下注册类别:public class NotifcationProducerRegistrations{ readonly ConcurrentDictionary<Type, Dictionary<Type, InstanceProducer>> handlerProducers = new ConcurrentDictionary<Type, Dictionary<Type, InstanceProducer>>(); public void AddProducer(Type notificationType, Type concreteType, InstanceProducer producer) { this.handlerProducers.AddOrUpdate( notificationType, (key) => new Dictionary<Type, InstanceProducer> { { concreteType, producer } }, (key, dictionary) => { dictionary.Add(concreteType, producer); return dictionary; }); } public IEnumerable<InstanceProducer> GetRegistrations(Type notificationType) { if(this.handlerProducers.TryGetValue(notificationType, out var dict)) { foreach (var kvp in dict) yield return kvp.Value; } }}并注册如下:public static NotifcationProducerRegistrations GetNotificationHandlerProducersTest(this Container container, Dictionary<Type, Type[]> registrationTypes, Lifestyle lifestyle){ var registrations = new NotifcationProducerRegistrations(); foreach (var registration in registrationTypes) { var concreteType = registration.Key; var notificationHandlerTypes = registration.Value; var interfaceTypes = concreteType.GetClosedTypesOf(typeof(INotificationHandler<>)); foreach(var filteredInterfaceType in interfaceTypes.Intersect(notificationHandlerTypes)) { registrations.AddProducer( filteredInterfaceType, concreteType, lifestyle.CreateProducer(filteredInterfaceType, concreteType, container)); } } return registrations;}关于我的评论,基于上述内容,我在特定类型的具体类型的生产者与其他通知类型的其他具体类型的数组之间具有1:1的关系.With reference to my comment, with the above I have a 1:1 relation between producer for a specific type of concrete type, within the array of other concrete types for that notification type.如上所述,我目前正在看到一个数组,而我认为(可能是错误的)我只需要一个生产者,类似于下图:As mentioned, I am currently seeing an array whereas i think believe (and may be wrong) that i should only need to get one producer, similar to the below map:推荐答案在您的情况下,请避免使用以下方法将INotificationHandler<T>注册直接添加到容器中:In your case, prevent adding the INotificationHandler<T> registrations directly to the container using:container.Register<FirstClass>();这样做将按其具体类型注册该类,这将禁止应用装饰器.取而代之的是,您通常会使用以下注册:Doing so would register the class by its concrete type, which disallows decorators from being applied. Instead, you would typically use the following registration:container.Register<INotificationHandler<Aggregate>, FirstClass>();它允许使用INotificationHandler<T>上的装饰器.但是,这在您的情况下仍然行不通,因为同一摘要的有多个实现.因此,您通常可以将其注册为一个集合,就像您在问题中提到的那样:It allows decorators on INotificationHandler<T> to be applied. This, however, still wouldn't work in your case, because there are multiple implementations of the same abstaction. So instead, you would normally register it as a collection, as you mentioned in your question:container.Collection.Register(typeof(INotificationHandler<>), typeof(FirstClass).Assembly);但这会很好地工作,并且允许您基于放置在实现上的属性来过滤返回的集合……只要您不使用装饰器包装实现即可,因为在这种情况下,您将检查属性在最外面的装饰器上.这显然是您想要实现的.But this would work well and allows you to filter a returned collection based on attributes that are placed on the implementation... as long as you don't wrap implementations using decorators, because in that case you would be checking the attributes on the outer-most decorator. This is obviously what you want to achieve.您寻求解决方案的解决方案是-不在Simple Injector的内部resolve-dictionary中注册这些组件(即使用Register调用之一),而是手动"创建InstanceProducer实例并将其存储到您自己中:The solution to your quest is to -not- register those components in Simple Injector's internal resolve-dictionary (i.e. using one of the Register calls), but instead create InstanceProducer instances 'manually' and store them into yourself:IEnumerable<Type> handlerTypes = container.GetTypesToRegister(typeof(INotificationHandler<>), typeof(FirstClass).Assembly);Dictionary<Type, Dictionary<Type, InstanceProducer>> handlerProducers = ( from handlerType in handlerTypes from interfaceType in handlerType.GetClosedTypesOf(typeof(INotificationHandler<>)) let producer = Lifestyle.Transient.CreateProducer(interfaceType, handlerType, container) group new { handlerType, producer } by interfaceType into interfaceGroup select new { MessageType = interfaceGroup.GetGenericArguments().Single(), Producers = interfaceGroup.ToDictionary(i => i.handlerType, i => i.producer) }) .ToDictionary(i => i.MessageType, i => i.Producers);对GetClosedTypesOf的调用将获取给定handlerType的所有INotificationHandler<T>封闭版本.一个处理程序可以实现多个接口,并且GetClosedTypesOf将返回handlerTyp实现的所有INotificationHandler<T>封闭版本.The call to GetClosedTypesOf gets all closed versions of INotificationHandler<T> for the given handlerType. A handler can implement multiple interfaces, and GetClosedTypesOf will return all closed versions of INotificationHandler<T> that the handlerTyp implements.必须使用interfaceType来创建InstanceProducer.这等效于调用Register(interfaceType, handlerType),因为它允许Simple Injector根据接口类型应用装饰器.That interfaceType must be used to create the InstanceProducer. This is the equivalent of calling Register(interfaceType, handlerType) as it allows Simple Injector to apply decorators based on the interface type.使用Lifestyle.CreateProducer创建的注册无法通过调用Container.GetInstance来解决,但它们仍是验证过程的一部分.就像任何其他注册一样,它们也经过了验证和诊断. InstanceProducer包含其自己的GetInstance方法,而不是调用Container.GetInstance.例如:Registrations created using Lifestyle.CreateProducer can't be resolved by calling Container.GetInstance, but they are still part of verification process. They are verified and diagnosed, just like any other registration. Instead of calling Container.GetInstance, an InstanceProducer contains its own GetInstance method. For instance:IEnumerable<INotificationHandler<TNotification>>> GetConcreteTypeThatImplementsNotificationHandlers<TNotification>(string topic) where TNotification : INotification{ var concreteTypes = TopicRegistrations.GetTypeForTopic(topic); var notificationHandlerProducers = this.handlerProducers[typeof(TNotification)]; foreach(var type in concreteTypes) { var instance = notificationHandlerProducers[type].GetInstance(); yield return (INotificationHandler<TNotification>>)instance; }} 这篇关于通过使用简单注入器应用的装饰器来获取单个指定混凝土类型的特定接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-24 00:54