简体   繁体   English

SimpleInjector如何为单个通用实现注册多个开放通用接口

[英]SimpleInjector HowTo Register multiple Open Generic Interfaces for a Single Generic Implementation

I'm trying to get started with SimpleInjector as an IOC Container and up to now I'm pretty happy with it. 我正在尝试使用SimpleInjector作为IOC容器,到目前为止我对此非常满意。 But right now I'm stuck on a problem I can't solve. 但是现在我遇到了一个我无法解决的问题。 I searched on SO and in the documentation, but it seems to be not answered yet. 我搜索了SO和文档,但似乎还没有回答。 I've seen the howto doc from SimpleInjector but that doesn't cover open generic interfaces. 我已经看过SimpleInjector的howto doc但是它没有涵盖开放的通用接口。

I have two generic interfaces like these: 我有两个这样的通用接口:

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

And one open generic implementation for those 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'm setting up SimpleInjector like this: 在我的应用程序中,我正在设置SimpleInjector,如下所示:

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();

What I'm trying to archive is: I'd like to get exactly the same instance when asking for a IEventPublisher or an IEventSubscriber. 我想要存档的是:在要求IEventPublisher或IEventSubscriber时,我想获得完全相同的实例。 And furthermore this Instance shall be a singleton for any T. 此外,此实例应为任何T的单例。

I've tested this with these lines: 我用这些线测试了这个:

class DummyEvent {}

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

Unfortunatly p and s don't refer to the same instance. 不幸的是,p和s并不是指同一个实例。 Anyone happens to know a solution to this problem? 有人碰巧知道这个问题的解决方案吗?

There are certain solutions for this, here's one: Create separate implementations for IEventPublisher<T> and IEventSubscriber<T> and let them delegate to the EventMediator<T> . 有一些解决方案,这里是一个:为IEventPublisher<T>IEventSubscriber<T>创建单独的实现,并让它们委托给EventMediator<T> For instance 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 make the registrations as follows: 现在您进行如下注册:

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

Now both the EventPublisher<DummyEvent> and EventSubscriber<DummyEvent> will point at the same EventMediator<DummyEvent> instance. 现在, EventPublisher<DummyEvent>EventSubscriber<DummyEvent>都将指向同一个EventMediator<DummyEvent>实例。

Another way to achieve this without the extra type is to make use of the ResolveUnregisteredType event (which is what the RegisterOpenGeneric extension method itself uses under the covers). 在没有额外类型的情况下实现此目的的另一种方法是使用ResolveUnregisteredType事件(这是RegisterOpenGeneric扩展方法本身在封面下使用的事件)。 Your configuration would 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 could even extract this code into a more general extension method. 您甚至可以将此代码提取到更通用的扩展方法中。 This way your registration would look like this: 这样您的注册将如下所示:

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

The extension method would 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);
            }
        }
    };
}

You are registering IEventPublisher and IEventSubscriber as separate singletons. 您正在将IEventPublisherIEventSubscriber注册为单独的单例。 You will need to refactor your code in one way or another. 您需要以某种方式重构代码。 One solution is to separate the 3 responsibilities of your mediator: 一种解决方案是分离调解员的3个职责:

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

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

The Subscriptions 订阅

public interface ISubscriptions<T> : IList<Action<T>> { }

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

Only the subscriptions need to be registered as singleton 只有订阅需要注册为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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM