ContainerExpressionBuilt Event

Simple Injector
Occurs after the creation of the Expression of a registered type is complete (the lifestyle has been applied), allowing the created Expression to be wrapped, changed, or replaced. Multiple delegates may handle the same service type.

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

public event EventHandler<ExpressionBuiltEventArgs> ExpressionBuilt

Value

Type: SystemEventHandlerExpressionBuiltEventArgs
Remarks

The ExpressionBuilt event is called by the container every time an registered type is getting compiled, allowing a developer to change the way the type is created. The delegate that hooks to the ExpressionBuilt event, can change the Expression property on the ExpressionBuiltEventArgs, which allows changing the way the type is constructed.

Thread-safety: Please note that the container will not ensure that the hooked delegates are executed only once per service type. While the calls to ExpressionBuilt 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 ExpressionBuilt event:
C#
public interface IValidator<T>
{
    void Validate(T instance);
}

public interface ILogger
{
    void Write(string message);
}

// Implementation of the decorator pattern.
public class MonitoringValidator<T> : IValidator<T>
{
    private readonly IValidator<T> validator;
    private readonly ILogger logger;

    public MonitoringValidator(IValidator<T> validator, ILogger logger)
    {
        this.validator = validator;
        this.logger = logger;
    }

    public void Validate(T instance)
    {
        this.logger.Write("Validating " + typeof(T).Name);
        this.validator.Validate(instance);
        this.logger.Write("Validated " + typeof(T).Name);
    }
}

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

    container.Register<ILogger, ConsoleLogger>(Lifestyle.Singleton);
    container.Register<IValidator<Order>, OrderValidator>();
    container.Register<IValidator<Customer>, CustomerValidator>();

    // Intercept the creation of IValidator<T> instances and wrap them in a MonitoringValidator<T>:
    container.ExpressionBuilt += (sender, e) =>
    {
        if (e.RegisteredServiceType.IsGenericType &&
            e.RegisteredServiceType.GetGenericTypeDefinition() == typeof(IValidator<>))
        {
            var decoratorType = typeof(MonitoringValidator<>)
                .MakeGenericType(e.RegisteredServiceType.GetGenericArguments());

            // Wrap the IValidator<T> in a MonitoringValidator<T>.
            e.Expression = Expression.New(decoratorType.GetConstructors()[0], new Expression[]
            {
                e.Expression,
                container.GetRegistration(typeof(ILogger)).BuildExpression(),
            });
        }
    };

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

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

The example above registers a delegate that is raised every time the container compiles the expression for an registered type. 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 changes the current Expression with a new one that creates a new MonitoringValidator<T> that takes the current validator (and an ILogger) as an dependency.

Please note that given example is just an uhhmm... example. In the case of the example the MonitoringValidator<T> is a decorator and instead of manually writing this code that many limitations, you can use one of the built-in RegisterDecorator methods instead. These extension methods take care of any given generic type constraint, allow to register decorators conditionally and allow the decorator 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