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.
- Nathanael Mann – Resolving Sitecore Controllers from your IoC Container
- Mickey Rahman – Custom SimpleInjector Dependency Resolver for Sitecore MVC
- Himadri Chakrabarti – Sitecore MVC and Unity
- Mickey Rahman – Changes to Dependency Injection in Sitecore 8.1
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/
Definitely like that the fallback here uses System.Web.Mvc.DependencyResolver, instead of Sitecore’s Type Creator – Great Post!
This is great Sean! I am currently trying out Simple Injector in a project and will try it out with that.
Thanks,
Chitrang
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?
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.
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.
Sure, will do soon.
Hey Sean, should I pull request to master or will you create a new branch for Simple Injector?
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!
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.
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.
Thank you for sharing this! This solution worked out perfectly for me!
Hi Jose,
Thank you very much, I really appreciate you come back to add this tip for WFFM I had the same problem.
Cheers
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
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
Great Post!
It was very helpful, I used it with AutoFac and SC9 and it works perfectly!
Hi Sean,
Great Post , We are using the ChainedResolver on 8.x Instance and after upgrading to 9.2 , we are getting an error “Constructor not found” for the controllers which have a default constructor(with arguments) defined. Any ideas on how to resolve this ? We are using Ninject BTW
Hi Abhay,
For my project using 8.2+ I’ve just used Sitecore’s out of the box dependency injection, as it allows the configuration of multiple configurators….so there’s no need for the Chained Resolver anymore.
Then, with that, the DI container is Microsoft’s light weight .NET Core one…. so it’s fast, and usable for most basic DI scenarios (i.e replacing an interface with a specified implementation).
Unfortunately I haven’t tried it with Ninject, so I’m not really sure how to resolve this for you.