Implementing IOC in Umbraco 7- Inversion of Control Like a Boss

I've devoted a significant amount of time this week to finding the best way to implement inversion of control for proper dependency injection in Umbraco.

After much brow-furrowing and much to-ing and fro-ing with some of the Umbraco devs (shout out to @clausjnsn for all the help) I think I have it.

I'm using Autofac because I've used it before, and @shazwazza's original IOC documentation on our.umbraco used it. If you're confident with your container's api you should be able to follow my instructions with any container you like.

Step 1: Setup

Step one is to add some custom startup code to Umbraco with a custom OnApplicationStarted method. For those familiar with dependency-injection lingo, this is our application's composition root, or where we will register the dependencies with our container. My preferred way to do this is by implementing IApplicationEventHandler. The mere presence of this class somewhere in your code should cause your OnApplicationStarted method to be run, well... on application started. If that makes any grammatical sense.

Step 2: Injecting an IContentService

Let's suppose we're doing some fancy stuff with Umbraco and have written a class that depends on ContentService. Nothing too fancy that the docs won't be able to help you with.

ApplicationContext.Current.Services.ContentService.CreateContent(name, parentID, contentTypeAlias);  

Your trusty copy of Mark Seeman's Dependency Injection in .NET should have just dislodged itself from the shelf, wiggled free, and hurled itself at the back of your head at the sight of the above code. I hope it hurt. How could you be so naive? You've just gone and created an explicit dependency on ApplicationContext.Current.Services.ContentService. How are you going to unit test that? What happens when your boss tells you to rewrite the whole thing with EntityFramework and you have explicit references to Umbraco services all over the place?

Since the content service implements IContentService anyway, you should be writing your own wrappers around Umbraco's core services, and then using your dependency injection mojo to inject an instance of IContentService into them. Here's an example of a "wrapper" class.

Notice how it doesn't depend on Umbraco's ContentService directly? It only depends on the interface IContentService. Providing our own implementation of IContentService for unit testing should now be a piece of cake. Depending on this class will appease the SOLID gods and guarantee you a place in programmer paradise.

We just need to make sure that our IContentService is being injected properly at our app's composition root, which takes only a few lines of code in our IApplicationEventHandler implementation.

This shouldn't be spectacular to you, but all we're doing is telling Autofac to use applicationContext.Services.ContentService, the instance of IContentService that's helpfully setup by Umbraco, whenever one of our classes asks for an IContentService as a dependency, like our MyAppContentService wrapper does in the constructor.

Lastly, we tell ASP.NET MVC to use Autofac as a dependency resolver.

    // Set up MVC to use Autofac as a dependency resolver
    var container = builder.Build();
    System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

Now you can live out the rest of your days in the endless bliss of dependency injection paradise.

OR CAN YOU?

Step 3: Trouble in Para-DI-se

If you're smart, you will have noticed that the above code has broken the Umbraco backoffice. If you're not smart, you will have shipped your code and upset your colleagues/clients/boss/deity. Well done. Yes, I'll have fries with that.

It looks like the code that sets the DependencyResolver for ASP.NET MVC causes the Umbraco backoffice to freak out when it tries to work with API controllers. Or something like that.

Happily, we can resolve this issue with very few lines of code by registering all the mvc controllers and web api controllers in Umbraco's assembly with our IOC. Autofac has quite a slick API for doing this.

In case you missed it, we added two LOC:

    builder.RegisterControllers(typeof(Umbraco.Web.UmbracoApplication).Assembly);
    builder.RegisterApiControllers(typeof(Umbraco.Web.UmbracoApplication).Assembly);

Step 4: .... Profit?

That's all there is to it folks. IOC in Umbraco. I've experience the pain of the many gotchas during this process so that you don't have to.

Thanks for reading! If you enjoyed this post, have anything to add, or would otherwise like to get in touch you can do so on Twitter @glcheetham.