Variance ExtensionsΒΆ

Allowing Simple Injector to resolve a variant registration.

The following code snippet adds the ability to Simple Injector to resolve an assignable variant implementation, in case the exact requested type is not registered.

using System;
using System.Linq;
using SimpleInjector;

public static class VarianceExtensions
{
    /// <summary>
    /// When this method is called on a container, it allows the container to map an
    /// unregistered requested (interface or delegate) type to an assignable and
    /// (interface or delegate) type that has been registered in the container. When
    /// there are multiple compatible types, an <see cref="ActivationException"/> will
    /// be thrown.
    /// </summary>
    /// <param name="ContainerOptions">The options to make the registrations in.</param>
    public static void AllowToResolveVariantTypes(this ContainerOptions options) {
        var container = options.Container;
        container.ResolveUnregisteredType += (sender, e) => {
            Type serviceType = e.UnregisteredServiceType;

            if (!serviceType.IsGenericType || e.Handled) return;

            var registrations = FindAssignableRegistrations(container, serviceType);

            if (!registrations.Any()) {
                // No registration found. We're done.
            }
            else if (registrations.Length == 1) {
                // Exactly one registration. Let's map the registration to the
                // unregistered service type.
                e.Register(registrations[0].Registration);
            } else {
                var names = string.Join(", ",
                    registrations.Select(r => r.ServiceType.ToFriendlyName()));

                throw new ActivationException(string.Format(
                    "There is an error in the container's configuration. It is impos" +
                    "sible to resolve type {0}, because there are {1} registrations " +
                    "that are applicable. Ambiguous registrations: {2}.",
                    serviceType.ToFriendlyName(), registrations.Length, names));
            }
        };
    }

    private static InstanceProducer[] FindAssignableRegistrations(Container container,
        Type serviceType) {
        Type serviceTypeDefinition = serviceType.GetGenericTypeDefinition();

        return (
            from reg in container.GetCurrentRegistrations()
            where reg.ServiceType.IsGenericType
            where reg.ServiceType.GetGenericTypeDefinition() == serviceTypeDefinition
            where serviceType.IsAssignableFrom(reg.ServiceType)
            select reg)
            .ToArray();
    }
}

After copying the previous code snippet to your project, you can use the extension method as follows:

var container = new Container();

container.Options.AllowToResolveVariantTypes();

// IEventHandler<in TEvent>
this.container.Register<IEventHandler<CustomerMovedEvent>, CustomerMovedEventHandler>();

// MoveCustomerAbroadEvent inherits from MoveCustomerEvent
var handler = this.container.GetInstance<IEventHandler<CustomerMovedAbroadEvent>>();

// Assert
Assert.IsInstanceOfType(handler, typeof(CustomerMovedEventHandler));