ContainerRegisterDecorator Method (Type, Type)

Simple Injector
Ensures that the supplied decoratorType decorator is returned, wrapping the original registered serviceType, by injecting that service type into the constructor of the supplied decoratorType. Multiple decorators may be applied to the same serviceType. Decorators can be applied to both open, closed, and non-generic service types. By default, a new decoratorType instance will be returned on each request (according the Transient lifestyle), independently of the lifestyle of the wrapped service.

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

public void RegisterDecorator(
	Type serviceType,
	Type decoratorType
)

Parameters

serviceType
Type: SystemType
The (possibly open generic) service type that will be wrapped by the given decoratorType.
decoratorType
Type: SystemType
The (possibly the open generic) decorator type that will be used to wrap the original service type.
Exceptions

ExceptionCondition
ArgumentNullExceptionThrown when one of the arguments is a null reference.
ArgumentExceptionThrown when serviceType is not an open generic type, when decoratorType does not inherit from or implement serviceType, when decoratorType does not have a single public constructor, or when decoratorType does not contain a constructor that has exactly one argument of type serviceType or FuncTResult where T is serviceType.
Remarks

This method uses the container's LifestyleSelectionBehavior to select the exact lifestyle for the specified type. By default this will be Transient.

The RegisterDecorator method works by hooking onto the container's ExpressionBuilt event. This event fires after the ResolveUnregisteredType event, which allows decoration of types that are resolved using unregistered type resolution.

Multiple decorators can be applied to the same service type. The order in which they are registered is the order they get applied in. This means that the decorator that gets registered first, gets applied first, which means that the next registered decorator, will wrap the first decorator, which wraps the original service type.

Constructor injection will be used on that type, and although it may have many constructor arguments, it must have exactly one argument of the type of serviceType, or an argument of type FuncTResult where TResult is serviceType. An exception will be thrown when this is not the case.

The registered decoratorType may have a constructor with an argument of type FuncTResult where T is serviceType. In this case, an decorated instance will not injected into the decoratorType, but it will inject a FuncTResult that allows creating instances of the decorated type, according to the lifestyle of that type. This enables more advanced scenarios, such as executing the decorated types on a different thread, or executing decorated instance within a certain scope (such as a lifetime scope).

Examples

The following example shows the definition of a generic ICommandHandler<T> interface, a CustomerMovedCommandHandler implementing that interface, and a ValidatorCommandHandlerDecorator<T> that acts as a decorator for that interface.
C#
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using SimpleInjector;
using SimpleInjector.Extensions;

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

public class CustomerMovedCommand
{
    [Required]
    public int CustomerId { get; set; }

    [Required]
    public Address Address { get; set; }
}

public class CustomerMovedCommandHandler
    : ICommandHandler<CustomerMovedCommand>
{
    public void Handle(CustomerMovedCommand command)
    {
        // some logic
    }
}

// Decorator that validates commands before they get executed.
public class ValidatorCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decoratedHandler;
    private readonly Container container;

    public ValidatorCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratedHandler,
        Container container)
    {
        this.decoratedHandler = decoratedHandler;
        this.container = container;
    }

    public void Handle(TCommand command)
    {
        this.Validate(command);

        this.decoratedHandler.Handle(command);
    }

    private void Validate(TCommand command)
    {
        var validationContext =
            new ValidationContext(command, this.container, null);

        Validator.ValidateObject(command, validationContext);
    }
}

// Decorator that measures the time it takes to execute a command.
public class MonitoringCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decoratedHandler;
    private readonly ILogger logger;

    public MonitoringCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratedHandler,
        ILogger logger)
    {
        this.decoratedHandler = decoratedHandler;
        this.logger = logger;
    }

    public void Handle(TCommand command)
    {
        var watch = Stopwatch.StartNew();

        this.decoratedHandler.Handle(command);

        this.logger.Log(string.Format("{0} executed in {1} ms.",
            command.GetType().Name, watch.ElapsedMilliseconds));
    }
}

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

    container.Register<ILogger, DebugLogger>(Lifestyle.Singleton);

    // Search the given assembly and register all concrete types that
    // implement ICommandHandler<TCommand>.
    container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
        typeof(ICommandHandler<>).Assembly);

    // Wrap all ICommandHandler<TCommand> service types with a decorator
    // that measures and logs the duration of that handler.
    container.RegisterDecorator(typeof(ICommandHandler<>),
        typeof(MonitoringCommandHandlerDecorator<>));

    // Wrap all ICommandHandler<TCommand> types (in this case it will
    // wrap the monitoring decorator), but only if the TCommand contains
    // any properties.
    container.RegisterDecorator(typeof(ICommandHandler<>),
        typeof(ValidatorCommandHandlerDecorator<>), context =>
        {
            var commandType = context.ServiceType.GetGenericArguments()[0];
            bool mustDecorate = commandType.GetProperties().Any();
            return mustDecorate;
        });

    // Act
    var handler =
        container.GetInstance<ICommandHandler<CustomerMovedCommand>>();

    // Assert
    Assert.IsInstanceOfType(handler,
        typeof(ValidatorCommandHandlerDecorator<CustomerMovedCommand>));
}
See Also

Reference