Sean Holmesby

.NET and Sitecore Developer

By

Safe Dependency Injection for MVC and WebApi within Sitecore

This post describes how we’ve considered approaching dependency injection with Sitecore at Hedgehog Development.
Many people have posted about dependency injection in Sitecore before, and each has had their own twist.

We’ve noticed that sometimes Sitecore controllers are failing to be resolved when there is a custom Dependency Resolver added.
The problem, as described in Mickey’s first post, is that MVC only allows for one DependencyResolver. So the moment you set your own Dependency Resolver, whatever was set there before (like whatever Sitecore set as the Dependency Resolver) will no longer work.
Because of this, Sitecore’s registered types will no longer get resolved, and you might see the following errors in the logs when you try to access one of the out-of-the-box Sitecore applications.

Exception: Sitecore.Mvc.Diagnostics.ControllerCreationException
Message: Could not create controller: 'Settings'.
The current route url is: 'api/sitecore/{controller}/{action}'. 
Source: Sitecore.Mvc
   at Sitecore.Mvc.Controllers.SitecoreControllerFactory.CreateController(RequestContext requestContext, String controllerName)

Message: No component for supporting the service Sitecore.Controllers.SettingsController was found

I spoke to Charlie Turano about this, and our solution was to create a ChainedMvcResolver, which will set a ‘Fallback Resolver’ to whatever the resolver was before we came along and set ours.

Then if our resolver can’t resolve the type, we’ll fallback and see if the fallback resolver can.
In this scenario, the fallback resolver will be what Sitecore set, so when we come across a SitecoreController, we don’t resolve it, and instead we leave it up to that DependencyResolver to figure out what it is.

    IDependencyResolver _fallbackResolver;
    IDependencyResolver _newResolver;
 
    public ChainedMvcResolver(IDependencyResolver newResolver, IDependencyResolver fallbackResolver)
    {
        _newResolver = newResolver;
        _fallbackResolver = fallbackResolver;
    }
 
    public object GetService(Type serviceType)
    {
        object result = null;
 
        result = _newResolver.GetService(serviceType);
        // Note: we assume the resolver returns null for unregistered types. This was found to perform better than
        // catching an exception, however this code could easily be modified to support a catch instead.
        if (result != null)
        {
            return result;
        }
 
        return _fallbackResolver.GetService(serviceType);
    }

To use it, we just create a new ChainedMvcResolver, passing in our resolver, and whatever DependencyResolver was previously set.

IDependencyResolver chainedMVCResolver = new ChainedMvcResolver(new WindsorMvcDependencyResolver(container),
                                                                    System.Web.Mvc.DependencyResolver.Current);
System.Web.Mvc.DependencyResolver.SetResolver(chainedMVCResolver);

This chaining method has been used before, but by chaining the ControllerFactory. Here, we’re just chaining the resolver instead.
The resolver issue has been seen before, and the idea of letting resolving the type elsewhere is not new. Chris van de Steeg’s SitecoreResolver in his BoC framework explicitly calls the Activator to create an instance. Similarly, in Mickey’s post above, he falls back by explicitly calling Sitecore.Mvc.Helpers.TypeHelper.CreateObject<IController> to get Sitecore to resolve it.

This certainly works, but the ChainedMvcResolver doesn’t assume the type passed in is a Controller, nor does it assume that Sitecore, (or anyone else) will be the one who should resolve it.

A note on ControllerFactories

Mickey posted how Sitecore changed how Dependency Injection was changed in Sitecore 8.1. Because the resolver is now being utilized, we can use the above fallback, and no longer need to create our own ControllerFactory for Sitecore to be able to resolve it’s own controllers.
This is fantastic, but be warned, if you’re using CastleWindsor as your DependencyResolver, you could potentially be causing memory leaks. (Thanks to Mike Edwards for the tip). In that case….maybe you should keep the Controller Factory, and release the controller correctly.

The WebApi Dependency Resolver Needs Attention Too

That’s right…. MVC and WebApi use different dependency resolvers.

However, the issue descibed above has also been noticed for WebApi with similar symptoms.
Brainjocks – WebAPI, SimpleInjector and AnalyticsDataController

In Pavel’s post above, the AnalyticsDataController was the one SimpleInjector struggled with (because it has multiple constructors).
But it made me wonder, what if there are other problematic Controllers in Sitecore’s code that mightn’t also get resolved properly with a custom DependencyResolver.

Again, we can do the chaining of DependencyResolvers for WebApi, the same way we did for MVC above.

IDependencyResolver chainedWebAPIResolver = new ChainedWebApiResolver(new WindsorWebApiDependencyResolver(container),
                                                                          GlobalConfiguration.Configuration.DependencyResolver);
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = chainedWebAPIResolver;

Note: Full implementation is given in the github repo link below.

Now Sitecore Controllers, either MVC or WebApi, can continue to be resolved the way Sitecore intends, and our controllers be be resolved then way we want them to.

Please see the full implementation on our GitHub repo.
https://github.com/HedgehogDevelopment/sitecore-chained-dependency-resolver

Please let me know what you think about this approach? Is there anything you like/hate/would do differently?

Further reading on Dependency Injection with MVC and WebApi

http://cardinalcore.co.uk/2015/03/17/sitecore-webapi-setting-up-ioc/
http://www.nttdatasitecore.com/Blog/2015/January/Dependency-Injection-using-Simple-Injector-for-Sitecore-8-WebApi-Attribute-Routing-in-APIControllers
http://laubplusco.net/simple-ioc-container-using-only-the-sitecore-api/

14 Responses to Safe Dependency Injection for MVC and WebApi within Sitecore

  1. Definitely like that the fallback here uses System.Web.Mvc.DependencyResolver, instead of Sitecore’s Type Creator – Great Post!

  2. This is great Sean! I am currently trying out Simple Injector in a project and will try it out with that.

    Thanks,
    Chitrang

  3. Jose says:

    Hey Sean, great post. I’ve got a question that hopefully you can help.

    I downloaded the code from github and implemented on my site and it’s all working fine (DI is working in sitecore controllers and webapi controllers) without the code you have in SetupDependencyResolvers.cs for:
    // Register MVC controllers
    container.Register(Classes.FromAssemblyNamed(“CustomController”)
    .BasedOn()
    .LifestyleTransient());
    // Register WebApi controllers

    ….

    What’s the purpose of these 2 registrations?

  4. Jose says:

    OK never mind -I understand why now. I’m using SimpleInjector and it comes with classes that mirror your classes: WindsorMvcDependencyResolver.cs and WindsorWebApiDependencyResolver.cs.

    So I didn’t need to build these 2 classes, nor WindsorWebApiDependencyScope.cs and also do not need to Register MVC controllers or WebAPI as it takes care of it.

    • sholmesby says:

      Yeah exactly, some IoC containers support these out of the box.
      I didn’t know that SimpleInjector also doesn’t require explicit registration of controllers. That’s pretty cool.

      Would you be able to submit your SimpleInjector code as a pull request? We’d love to get more IoC container support for this, and SimpleInjector is a pretty popular one.

  5. Jose says:

    Sure, will do soon.

  6. Jose says:

    Hey Sean, should I pull request to master or will you create a new branch for Simple Injector?

    • sholmesby says:

      If you can create it in a new ‘feature-SimpleInjector’ branch that would be ideal.
      I haven’t really figured out what branching strategy to do for this yet, but let’s put it there for now and I’ll work something out after the pull request.
      Thanks for contributing!

  7. Jose says:

    Hey Sean,

    Was getting the following error after installing WFFM.
    Exception: System.Web.HttpException
    Message: The controller for path ‘/’ was not found or does not implement IController.
    Source: System.Web.Mvc

    So instead of replacing Sitecore’s IoC, I’m now SetupDependencyResolvers by replacing the patch to patch:after when adding pipeline.

  8. Shafiq says:

    Hi Sean,

    Great post…. My application uses dependency injection through Castle Windsor. recently I upgraded from 7.1 to 8.1 and faced API not working…. found many solution but nothing worked. Your solution worked to fix my API problem. Many thanks again.

  9. Doug says:

    Thank you for sharing this! This solution worked out perfectly for me!

  10. Fernando says:

    Hi Jose,

    Thank you very much, I really appreciate you come back to add this tip for WFFM I had the same problem.

    Cheers

  11. Bang says:

    Hi Sean,

    I migrate the projects to Sitecore.NET 8.2 (rev. 170407).

    Do you know why context.Request.GetDependencyScope().GetService(typeof(ILogger)) return null

    for /api/Custom/MyCustomWebApi/MyAction (custom route to the WebApi controller)
    or /api/Custom/MyCustomWebApi/MyActionWithFilter
    ?

    namespace CustomController.Filters
    {
    public class LoggingFilterAttribute : ActionFilterAttribute
    {
    public override void OnActionExecuting(HttpActionContext context)
    {
    var logger = context.Request.GetDependencyScope().GetService(typeof(ILogger)) as ILogger;
    //logger.Info(“Executing action.”);
    }
    }
    }

    Thanks,
    Bang

    • sholmesby says:

      I have not tried this on Sitecore 8.2, as 8.2 uses a built-in Chained Dependency Resolver.
      In 8.2, you can either use Sitecore’s new IoC features to setup your contract registrations, or set your own custom resolver before they set theirs (as theirs falls back to anything previous set).

      – Sean

Leave a Reply

Your email address will not be published. Required fields are marked *