Deciding on the best way to implement Inversion of Control in Umbraco has been the source of much musing and head-scratching among the community for as long as I have been in it. When I first arrived at Crumpled Dog, back in May, I was delighted to discover that here, this was a problem they had effectively solved using LightInject. In this article, I hope to be able to share some of the ways you can make it work for you too. I’ll take you through the rationale, the implementation and the benefits of choosing LightInject as your container for IoC.
By way of introduction to Lightinject, let me do a bit of explaining. LightInject is an ultra lightweight IoC container that supports the most common features expected from a service container. But when I first started writing this article, I couldn’t have told you that.
When I decided to write for Skrift, I was faced with a problem some of you can probably identify. Should I write what I know? Take the ever-so-comfortable route of waxing lyrical about the kind of topic you’d find me bending ears about at the pub; spew forth fifteen hundred words on a rant or a utopic vision so well-trodden that I might just end up looking good? Sounds interesting. Or should I take this article, a situation decidedly more public than most classrooms, as a learning opportunity to write what I want to know? You can probably tell from the title of this very piece that I ditched the former and went with the latter.
Personally, I like a deadline. A deadline pulls things into focus and forces you to get acquainted with something you may have been tempted to put off. Programming is a funny old business too. There are things that I work with, daily, that I don’t fully understand. There are things that I can get working, with a bit of tinkering, that I couldn’t implement from scratch. Most of us work for someone, a client, a boss. Essentially this means that most days I am faced with an experience I’m sure we are all pretty familiar with: something I didn’t build has broken or not broken but needs improving anyway. I need to get in, work out what’s going on and get it working the way we’d like it to be working.
A deadline pulls things into focus and forces you to get acquainted with something you may have been tempted to put off.
This approach has meant that over the course of my so-far pretty short career I’ve gotten elbow deep into a myriad of other people’s code. Some of it has been horrifying - tightly coupled beasts that you fix in one place and it breaks somewhere else. There have been occasions where, much to my chagrin, I’ve had to hash out a solution in keeping with that environment (just as an aside, learning that I wouldn’t be able to rip the guts out of these projects and start all over again every time I began to work on something came as a great shock to me).
On the flip side, and here’s what I want to write about today, I’ve delved into elegant solutions, ready to get involved and found that what I’m looking for doesn’t live where I thought it would. In fact, when I do find it, it doesn’t even look like I thought it would. And when faced with that scenario, I have done what any good developer would do: looked for patterns, sussed out behaviours, stepped through debug mode with an excessive number of breakpoints, done copious amounts of printing to screen and fashioned a solution that ‘looks’ like the rest of the code.
Now in an ideal world, the next logical thing I would do would be to find out what it is I’m looking at. Right? Get acquainted with how it works and why we’ve built this way. The world being far from ideal (I mean, where to even begin on the world?), that’s not always what happens next. More often than I’d like to admit, I’ve moved onto the next task and I made a little note in my ever-growing ‘Things I should Revisit’ document on Google Docs. Well, now is that time and here is what I learnt.
Inversion of control
The architecture of software design characterised by the inversion of the flow of control.
I learned about the SOLID principles when I first started coding. An old-colleague and sometime friend (you know who you are) said that it was something best learned early on, in order to keep my code clean as I grow as a developer. He told me I should consider these principles in anything I do, whether it be a small fix or a from-scratch shiny new build. I will happily admit that at that stage in my career as a developer, I was just happy if I could get a thing to work. I knew that there were such things as ‘elegant solutions’ but I certainly didn’t know if mine were shining examples of that or if they were just desperate hacks. So when I first read I made it all the way to the ‘D’, I found it perplexing. So with that in mind I’m going to do you all a favour and talk a little bit about what dependency inversion means, philosophically. For the uninitiated, this might just clear some things up for you. The pros can probably skip the next paragraph. Who am I kidding? You’ll have already skipped to the code snippets.
Dependency inversion is the idea entities should depend on abstractions wherever possible, rather than concretions. This allows you to decouple your code so that classes, rather than relying on each other, now rely on interfaces. Why would we want to do this? Well, the way it was explained to me, by a very patient colleague, was that if Class A depends on Class B and we decide to make a change to Class B, we are probably going to affect Class A.
This is all well and good, right? But when we are writing code how do we ensure we follow this principle? Well, one way we have done that here at my new(ish) home is by using LightInject as a container for IoC in all of our recent builds.
Getting started the right way
The structure of a build at its inception dictates the way it’s going to grow. The ways you will be constricted when a change is requested, the foundation of your future woes. As Umbraco has been developed over the years, it has become easier and easier to ensure that you follow ‘best practises’ when you build. Umbraco do not include IoC in the source code and this allows us as developers to implement whichever solution suits us best. The CMS gets better and better at ‘getting out of the way’, as it were. Now in Version 8, LightInject will be the engine that they do use to implement IoC. This helped us to make the decision as to which engine we should use. Still, we did some research and found that it was the fastest and most lightweight of the solutions that we looked at. After some tinkering (technical term, that is) we found that it suited our purposes beautifully.
For more information on the benchmark tests, have a look here at this fantastic blog on the performance times of various IoC containers.
When we decided to implement we started by playing with a dummy project, and testing the practicalities of the implementation. We got ourselves a new Umbraco project installed with a starter kit to get going. Once that was all good and installed, we enabled Models Builder in Models Factory mode. We created a homepage with a single property - a title with a textstring property editor. And then installed LightInject.
Installing LightInject was a simple process in and of itself. It’s available on Nuget so we just fired up the Nuget console and started the install with this command:
Install-Package LightInject
This installs the Binary version of the engine. Once that’s in place, you can start setting up IoC for the project.
We begin by creating a new class that inherits from IApplicationEventHandler so the code is run on startup of the application. We set up the using statements - in this case we will need both:
using LightInject;
using LightInject.Mvc;
To create your container, once LightInject is installed it’s really as simple as just newing one up. In this case,
var container = new ServiceContainer();
Then, you get your assemblies using a foreach statement and for each of those assemblies, you can register a controller type with it’s own container. So here we have used the following logic to do that:
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
//gets each of the assemblies
var controllerTypes = assembly.GetTypes().Where(t => !t.IsAbstract &&
typeof(IHttpController).IsAssingnableForm(t));
//registers the types we need for LightInject
foreach (var controllerType in controllerTypes)
{
container.Register(controllerType, new PerRequestLifeTime());
}
}
That set up, there are still a few steps to complete before we get stuck into our shiny new project.
We enable LightInject to use MVC mode:
container.EnableMvc();
We make sure that we allow LightInject to create a new container for each Web request:
container.EnableWebRequestScope();
And we allow WebApi mode too passing in our site’s configuration as we do:
container.EnableWebApi(GlobalConfiguration.Configuration);
And we mustn’t forget to resolve the dependencies using DependencyResolver:
DependencyResolver.SetResolver(new LightInjectMvcDependencyResolver(container));
After the set-up, we can go about the business of registering our interfaces to their concrete models. Like so:
var container = new ServiceContainer();
container.Register(typeof(IEmailManager), typeof(EmailManager));
And then you’re able to get your object in any constructor:
private readonly IEmailManager _emailManager;
public PasswordResetController(IEmailManager emailManager)
{
this._emailManager = emailManager;
}
Now, the wonderful thing about doing things this way is that once you’ve registered the interfaces you can call them from anywhere. This means that you only have to instantiate the class once so if you’re logic is particularly expensive, this isn’t going to hit the project’s overall performance too hard. Equally, there’s no longer a need for Singletons when you're setting up your unit testing.
Broken down like this, it all seems terribly simple, right? I can say from experience that while it took me some time to bend my mind around the whys and hows of this particular way of working, I have found it a pleasure to work with.
So here ends my first technical article. I hope it’s been of use to you. I can certainly say that it’s been of use to me and that, after many talks with colleagues, pages and pages of reading and 1500 or so words of writing, LightInject is one thing that I can now strike from my list.
For further reading on IoC Version 8, please see below: