ContainerResolveUnregisteredType Event

Simple Injector
Occurs when an instance of a type is requested that has not been registered explicitly, allowing resolution of unregistered types before the container tries to create the type.

Namespace:  SimpleInjector
Assembly:  SimpleInjector (in SimpleInjector.dll) Version: 5.3.0
Syntax

public event EventHandler<UnregisteredTypeEventArgs> ResolveUnregisteredType

Value

Type: SystemEventHandlerUnregisteredTypeEventArgs
Remarks

The ResolveUnregisteredType event is called by the container every time an unregistered type is requested for the first time, allowing a developer to do unregistered type resolution. By calling the Register method on the UnregisteredTypeEventArgs, a Registration, Expression or FuncTResult delegate can be registered allowing the container to retrieve instances of the requested type. This registration is cached and it prevents the ResolveUnregisteredType event from being called again for the same type.

When no registered event handled the registration of an unregistered type, the container will try to create the type when this type is either concrete or is the IEnumerableT interface. Concrete types will be registered with the Transient lifestyle and IEnumerableT registrations will return an empty collection. When no even handled the registration and the container could not create it, an exception is thrown.

Thread-safety: Please note that the container will not ensure that the hooked delegates are executed only once. While the calls to ResolveUnregisteredType for a given type are finite (and will in most cases happen just once), a container can call the delegate multiple times and make parallel calls to the delegate. You must make sure that the code can be called multiple times and is thread-safe.

Examples

The following example shows the usage of the ResolveUnregisteredType event:
C#
public interface IValidator<T>
{
    void Validate(T instance);
}

// Implementation of the null object pattern.
public class EmptyValidator<T> : IValidator<T>
{
    public void Validate(T instance)
    {
        // Does nothing.
    }
}

[TestMethod]
public void TestResolveUnregisteredType()
{
    // Arrange
    var container = new Container();

    // Register an EmptyValidator<T> to be returned when a IValidator<T> is requested:
    container.ResolveUnregisteredType += (sender, e) =>
    {
        if (e.UnregisteredServiceType.IsGenericType &&
            e.UnregisteredServiceType.GetGenericTypeDefinition() == typeof(IValidator<>))
        {
            var validatorType = typeof(EmptyValidator<>).MakeGenericType(
                e.UnregisteredServiceType.GetGenericArguments());

            // Register the instance as singleton.
            e.Register(Lifestyle.Singleton.CreateRegistration(validatorType, container));
        }
    };

    // Act
    var orderValidator = container.GetInstance<IValidator<Order>>();
    var customerValidator = container.GetInstance<IValidator<Customer>>();

    // Assert
    Assert.IsInstanceOfType(orderValidator, typeof(EmptyValidator<Order>));
    Assert.IsInstanceOfType(customerValidator, typeof(EmptyValidator<Customer>));
}

The example above registers a delegate that is raised every time an unregistered type is requested from the container. The delegate checks whether the requested type is a closed generic implementation of the IValidator<T> interface (such as IValidator<Order> or IValidator<Customer>). In that case it will request the container for a concrete EmptyValidator<T> implementation that implements the given UnregisteredServiceType, and registers a delegate that will return this created instance. The e.Register call registers the method in the container, preventing the ResolveUnregisteredType from being called again for the exact same service type, preventing any performance penalties.

Please note that given example is just an uhhmm... example. In the case of the example the EmptyValidator<T> can be better registered using of the built-in Register methods instead. These methods take care of any given generic type constraint and allow the implementation to be integrated into the container's pipeline, which allows it to be intercepted using the ExpressionBuilding event and allow any registered initializers to be applied.

See Also

Reference