Search code examples
c#simple-injectormediatormediatr

Mediator pattern and contravariance with Simple Injector


This question originates from the fact that I'm trying to create a Simple Injector implementation for MediatR: https://github.com/jbogard/MediatR/pull/14.

I'm having trouble while trying to resolve implementations of a generic handler interface. Consider the following notification handler interface:

public interface INotificationHandler<in TNotification>
    where TNotification : INotification
{
    void Handle(TNotification notification);
}

INotifcation is just an empty marker interface.

I defined the following handlers for the Pinged (which implements INotification) event:

public class PingedHandler : INotificationHandler<Pinged>
{
    public void Handle(Pinged notification) { }
}

public class PingedHandler2 : INotificationHandler<Pinged>
{
    public void Handle(Pinged notification) { }
}

And also a generic handler (notice this should handle every INotification):

public class GenericHandler : INotificationHandler<INotification>
{
    public void Handle(INotification notification) { }
}

With the following registration:

var container = new Container();

container.RegisterManyForOpenGeneric(
    typeof (INotificationHandler<>),
    (service, impls) => container.RegisterAll(service, impls),
    AppDomain.CurrentDomain.GetAssemblies());

Now I expect:

GetAllInstances<INotificationHandler<Pinged>>();

to resolve both PingedHandler and PingedHandler2 which it does. But it doesn't resolve the GenericHandler since it implements INotificationHandler<INotification> and not INotificationHandler<Pinged>. I wonder if there is a way to let Simple Injector search up the whole object graph and resolve anything that is Pinged too.

I've found a blog post from Steven about co-variance and contra-variance, but I'm unable to get it working for my example.


Solution

  • tl;dr: it was a bug/shortcoming in Simple Injector v2.6.0, which is fixed in v2.7.0. The configuration in the question (and shown below) does work now.


    To summarize @qujck's answer and @Steven's comment: I was able to get it working by installing Simple Injector v2.7.0-beta2 with the following configuration (actually the same as in the question):

    // Simple Injector v3.x
    container.RegisterCollection(typeof(INotificationHandler<>),
        AppDomain.CurrentDomain.GetAssemblies());
    
    // Simple Injector v2.x
    container.RegisterManyForOpenGeneric(
        typeof(INotificationHandler<>),
        container.RegisterAll,
        AppDomain.CurrentDomain.GetAssemblies());
    

    Now Simple Injector is able to resolve PingedHandler, PingedHandler2 and the GenericHandler when requesting:

    container.GetAllInstances<INotificationHandler<Pinged>>();