SimpleInjector How to Register Multiple Open Shared Interfaces for a Single Shared Implementation

I am trying to get started with SimpleInjector as an IOC container, and so far I'm happy with that. But now I am stuck in a problem that I cannot solve. I searched on SO and in the documentation, but didn't seem to answer yet. I saw howto doc from SimpleInjector , but it does not cover open shared interfaces.

I have two common interfaces:

public interface IEventPublisher<TEvent> { void Publish(TEvent Event); } public interface IEventSubscriber<TEvent> { void Subscribe(Action<TEvent> CallBack); } 

And one open common implementation for these two:

 class EventMediator<T> : IEventPublisher<T>, IEventSubscriber<T> { List<Action<T>> Subscriptions = new List<Action<T>>(); public void Publish(T Event) { foreach (var Subscription in this.Subscriptions) Subscription.Invoke(Event); } public void Subscribe(Action<T> CallBack) { this.Subscriptions.Add(CallBack); } } 

In my application, I configure SimpleInjector as follows:

 this.Container = new SimpleInjector.Container(); this.Container.RegisterOpenGeneric(typeof(IEventPublisher<>), typeof(EventMediator<>), Lifestyle.Singleton); this.Container.RegisterOpenGeneric(typeof(IEventSubscriber<>), typeof(EventMediator<>), Lifestyle.Singleton); this.Container.Verify(); 

I am trying to archive: I would like to get the exact same instance when requesting IEventPublisher or IEventSubscriber. And, in addition, this instance must be single for any T.

I tested this with the following lines:

 class DummyEvent {} var p = this.Container.GetInstance<IEventPublisher<DummyEvent>>(); var s = this.Container.GetInstance<IEventSubscriber<DummyEvent>>(); var areSame = (object.ReferenceEquals(p,s)); 

Unfortunately, p and s do not belong to the same instance. Does anyone know how to solve this problem?

+6
source share
2 answers

There are certain solutions for this, here is one: create separate implementations for IEventPublisher<T> and IEventSubscriber<T> and let them delegate the EventMediator<T> command. For example, with these implementations:

 public class EventPublisher<TEvent> : IEventPublisher<TEvent> { private readonly EventMediator<TEvent> mediator; public EventPublisher(EventMediator<TEvent> mediator) { this.mediator = mediator; } public void Publish(TEvent Event) { this.mediator.Publish(Event); } } public class EventSubscriber<TEvent> : IEventSubscriber<TEvent> { private readonly EventMediator<TEvent> mediator; public EventSubscriber(EventMediator<TEvent> mediator) { this.mediator = mediator; } public void Subscribe(Action<TEvent> CallBack) { this.mediator.Subscribe(Callback); } } 

Now you do the registration as follows:

 container.RegisterSingleOpenGeneric(typeof(EventMediator<>), typeof(EventMediator<>)); container.RegisterSingleOpenGeneric(typeof(IEventPublisher<>), typeof(EventPublisher<>)); container.RegisterSingleOpenGeneric(typeof(IEventSubscriber<>), typeof(EventSubscriber<>)); 

Now both EventPublisher<DummyEvent> and EventSubscriber<DummyEvent> will point to the same instance of EventMediator<DummyEvent> .

Another way to achieve this without an additional type is to use the ResolveUnregisteredType event (which itself uses the RegisterOpenGeneric extension method under covers). Your configuration will look like this:

 container.RegisterSingleOpenGeneric(typeof(EventMediator<>), typeof(EventMediator<>)); container.ResolveUnregisteredType += (s, e) => { if (e.UnregisteredServiceType.IsGenericType) { var def = e.UnregisteredServiceType.GetGenericTypeDefinition(); if (def == typeof(IEventPublisher<>) || def == typeof(IEventSubscriber<>)) { var mediatorType = typeof(EventMediator<>) .MakeGenericType(e.UnregisteredServiceType.GetGenericArguments()[0]); var producer = container.GetRegistration(mediatorType, true); e.Register(producer.Registration); } } }; 

You can even extract this code into a more general extension method. Thus, your registration will look like this:

 container.RegisterSingleOpenGeneric(typeof(EventMediator<>), typeof(EventMediator<>)); container.ForwardOpenGenericTo(typeof(IEventPublisher<>), typeof(EventMediator<>)); container.ForwardOpenGenericTo(typeof(IEventSubscriber<>), typeof(EventMediator<>)); 

The extension method will look like this:

 public static void ForwardOpenGenericTo(this Container container, Type openGenericServiceType, Type openGenericServiceTypeToForwardTo) { container.ResolveUnregisteredType += (s, e) => { var type = e.UnregisteredServiceType; if (type.IsGenericType) { if (type.GetGenericTypeDefinition() == openGenericServiceType) { var forwardToType = openGenericServiceTypeToForwardTo.MakeGenericType( type.GetGenericArguments()); var producer = container.GetRegistration(forwardToType, true); e.Register(producer.Registration); } } }; } 
+5
source

You register IEventPublisher and IEventSubscriber as separate singletones. You will need to reorganize your code anyway. One solution is to share the 3 responsibilities of your reseller:

Subscriber

 public interface IEventSubscriber<TEvent> { void Subscribe(Action<TEvent> CallBack); } public class EventSubscriber<T> : IEventSubscriber<T> { public readonly ISubscriptions<T> subscriptions; public EventSubscriber(ISubscriptions<T> subscriptions) { this.subscriptions = subscriptions; } public void Subscribe(Action<T> CallBack) { this.subscriptions.Add(CallBack); } } 

Publisher

 public interface IEventPublisher<TEvent> { void Publish(TEvent Event); } public class EventPublisher<T> : IEventPublisher<T> { public readonly ISubscriptions<T> subscriptions; public EventPublisher(ISubscriptions<T> subscriptions) { this.subscriptions = subscriptions; } public void Publish(T Event) { foreach (var subscription in this.subscriptions) { subscription.Invoke(Event); } } } 

Subscriptions

 public interface ISubscriptions<T> : IList<Action<T>> { } public class Subscriptions<T> : List<Action<T>>, ISubscriptions<T> { } 

Only subscriptions should be registered as singleton

 var container = new Container(); container.RegisterOpenGeneric(typeof(IEventSubscriber<>), typeof(EventSubscriber<>)); container.RegisterOpenGeneric(typeof(IEventPublisher<>), typeof(EventPublisher<>)); container.RegisterSingleOpenGeneric(typeof(ISubscriptions<>), typeof(Subscriptions<>)); container.Verify(); var p = container.GetInstance<IEventPublisher<DummyEvent>>(); var s = container.GetInstance<IEventSubscriber<DummyEvent>>(); Assert.That( (p as EventPublisher<DummyEvent>).subscriptions == (s as EventSubscriber<DummyEvent>).subscriptions); 
+3
source

Source: https://habr.com/ru/post/971418/


All Articles