ContainerRegisterInitializerTService Method (ActionTService)

Simple Injector
Registers an ActionT delegate that runs after the creation of instances that implement or derive from the given TService. Please note that only instances that are created by the container (using constructor injection) can be initialized this way.

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

public void RegisterInitializer<TService>(
	Action<TService> instanceInitializer
)
where TService : class

Parameters

instanceInitializer
Type: SystemActionTService
The delegate that will be called after the instance has been constructed and before it is returned.

Type Parameters

TService
The type for which the initializer will be registered.
Exceptions

ExceptionCondition
ArgumentNullException Thrown when the instanceInitializer is a null reference.
InvalidOperationException Thrown when this container instance is locked and can not be altered.
Remarks

Multiple instanceInitializer delegates can be registered per TService and multiple initializers can be applied on a created instance, before it is returned. For instance, when registering a instanceInitializer for type Object, the delegate will be called for every instance created by the container, which can be nice for debugging purposes.

Note: Initializers are guaranteed to be executed in the order they are registered.

The following example shows the usage of the RegisterInitializer method:

C#
public interface ITimeProvider { DateTime Now { get; } }
public interface ICommand { bool SendAsync { get; set; } }

public abstract class CommandBase : ICommand
{
    ITimeProvider Clock { get; set; }

    public bool SendAsync { get; set; }
}

public class ConcreteCommand : CommandBase { }

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

    container.Register<ICommand, ConcreteCommand>();

    // Configuring property injection for types that implement ICommand:
    container.RegisterInitializer<ICommand>(command =>
    {
        command.SendAsync = true;
    });

    // Configuring property injection for types that implement CommandBase:
    container.RegisterInitializer<CommandBase>(command =>
    {
        command.Clock = container.GetInstance<ITimeProvider>();
    });

    // Act
    var command = (ConcreteCommand)container.GetInstance<ICommand>();

    // Assert
    // Because ConcreteCommand implements both ICommand and CommandBase,
    // both the initializers will have been executed.
    Assert.IsTrue(command.SendAsync);
    Assert.IsNotNull(command.Clock);
}

The container does not use the type information of the requested service type, but it uses the type information of the actual implementation to find all initialized that apply for that type. This makes it possible to have multiple initializers to be applied on a single returned instance while keeping performance high.

Registered initializers will only be applied to instances that are created by the container self (using constructor injection). Types that are newed up manually by supplying a FuncTResult delegate to the container (using the RegisterTService(FuncTService) method) or registered as single instance (using RegisterInstanceTService(TService)) will not trigger initialization. When initialization of these instances is needed, this must be done manually, as can be seen in the following example:

C#
[TestMethod]
public static void TestRegisterInitializer()
{
    // Arrange
    int initializerCallCount = 0;

    var container = new Container();

    // Define a initializer for ICommand
    Action<ICommand> commandInitializer = command =>
    {
        initializerCallCount++;
    });

    // Configuring that initializer.
    container.RegisterInitializer<ICommand>(commandInitializer);

    container.Register<ICommand>(() =>
    {
        // Create a ConcreteCommand manually: will not be initialized.
        var command = new ConcreteCommand("Data Source=.;Initial Catalog=db;");

        // Run the initializer manually.
        commandInitializer(command);

        return command;
    });

    // Act
    var command = container.GetInstance<ICommand>();

    // Assert
    // The initializer will only be called once.
    Assert.AreEqual(1, initializerCallCount);
}
The previous example shows how a manually created instance can still be initialized. Try to prevent creating types manually, by changing the design of those classes. If possible, create a single public constructor that only contains dependencies that can be resolved.

See Also

Reference