Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to decorate an ASP.NET MVC controller with Simple Injector

I'd like to apply some cross-cutting concerns to my MVC controllers. At the moment, this is implemented through an abstract base class, but as we are refactoring more of the code base to take advantage of dependency injection, I'm wondering if this is something Simple Injector can help me with through its decoration or interception facilities.

So I've attempted to create a pretty basic decorator:

public class ControllerDecorator : IController
{
    private readonly IController _controller;

    public ControllerDecorator(IController controller)
    {
        _controller = controller;
    }

    public void Execute(RequestContext requestContext)
    {
        // Do something of a cross-cutting nature here...

        _controller.Execute(requestContext);
    }
}

And in my composition root: container.RegisterDecorator<IController, ControllerDecorator>()

However, the code in my decorator's Execute method doesn't seem to ever get called. Is it because the MVC framework directly resolves my controller classes instead of going through IController? In that case, what can I do? What am I missing here?

like image 876
Martin Wedvich Avatar asked Aug 20 '15 12:08

Martin Wedvich


1 Answers

In the default configuration, you can't apply decorators to controllers. The reason for this is that MVC's DefaultControllerFactory requests controllers by their concrete type. Because it requests a concrete type, Simple Injector is unable to apply a decorator; it has to assume that the caller needs this concrete type and has to therefore return this exact type (or a sub type).

To fix this, you will have to replace the default DefaultControllerFactory with a custom one:

public class SimpleInjectorControllerFactory : DefaultControllerFactory {
    public IDictionary<Type, InstanceProducer> Producers { get; set; }
    protected override IController GetControllerInstance(RequestContext rc, Type type) {
        return (IController)this.Producers[type].GetInstance();
    }
}

Next, in your bootstrapper, you'll have to replace the call to RegisterMvcControllers with the following:

var controllerTypes = SimpleInjectorMvcExtensions.GetControllerTypesToRegister(
    container, Assembly.GetExecutingAssembly());

var controllerProducers = controllerTypes
    .ToDictionary(type => type, type => CreateControllerProducer(container, type));

// Verify after creating the controller producers.
container.Verify();

ControllerBuilder.Current.SetControllerFactory(
    new SimpleInjectorControllerFactory { Producers = controllerProducers });

The CreateControllerProducer method looks as follows:

private static InstanceProducer CreateControllerProducer(Container c, Type type) {
    var producer = Lifestyle.Transient.CreateProducer(typeof(IController), type, c);
    producer.Registration.SuppressDiagnosticWarning(
        DiagnosticType.DisposableTransientComponent,
        "MVC disposes the controller when the web request ends.");
    return producer;
}

The crucial part is that the call to CreateProducer is supplied with typeof(IController); this allows Simple Injector to apply a decorator for IController.

This is it; now you can register your decorator for IController.

One warning though: with both Web API and the new ASP.NET core it is impossible to apply decorators to controllers. Both frameworks expect concrete types; they will break if you wrap the real controller. The preferred way with those frameworks to decorate controllers is through the OWIN pipeline. So this answer solely works with MVC 3, 4 and 5.

like image 173
Steven Avatar answered Oct 17 '22 04:10

Steven