Easily setup your Umbraco installation with IoC / Dependency Injection
Umbraco supports allowing you to setup and configure any IoC container type that you want to use in your application. For a while now there’s been some sparse documentation on how to achieve this which you can find here: https://our.umbraco.org/Documentation/reference/using-ioc. As the Umbraco core codebase evolves, sometimes a new non-parameterless constructor is added to a class and sometimes this can confuse an existing container that you’ve setup. For many folks, fixing these errors after upgrading is a trial and error experience until they track down the dependency that is now missing from their container and finally add it.
Simone, a very helpful Umbracian, made a comment on the issue tracker and it’s something that is just so obvious (http://issues.umbraco.org/issue/U4-9562#comment=67-41855):
I think the point here is: as user of a framework, I shouldn't need to wire up dependencies for internals of the framework myself. I should only bother about my own dependencies.
Maybe Umbraco should ship a small extension method for each of the main IoC container out there which wires up all the internals.
Or come with a IoC container out of the box and then everyone using umbraco have to use that one.
Yes of course this should be done!
A new community project: Our.Umbraco.IoC
I decided to get the ball rolling with this one and have setup a new Git repo here: https://github.com/Shazwazza/Our.Umbraco.IoC
Currently there are 2 different container configurations committed and working for Autofac and LightInject.
I’ve added some notes to the readme on how to contribute and get started so I’m hoping that some folks can create some Pull Requests to add support for more containers. The project is very easy to navigate, it’s got a build script and nuget packages setup.
Give it a go!
I’ve published some beta’s to Nuget:
Install-Package Our.Umbraco.IoC.Autofac
Install-Package Our.Umbraco.IoC.LightInject
You can actually install both and test each one independently by disabling them by an appSetting:
<add key="Our.Umbraco.IoC.Autofac.Enabled" value="false" />
Or
<add key="Our.Umbraco.IoC.LightInject.Enabled" value="false" />
If this config key doesn’t exist, it will assume the value is “true”
Using the container
Once you’ve got your desired package installed, it will be active in your solution (unless you disable it via config). At this stage you’ll want to add your own bits to the container, so here’s how you do that:
- Create a custom Umbraco ApplicationEventHandler
- Override ApplicationInitialized – we do this in this phase to bind to the container event before the container is built which occurs in the ApplicationStarted phase
- Bind to the container event
- add any custom services you want to the container
Here’s a full working example showing various techniques and includes the syntax for both LightInject and Autofac. In this example we’re registering a IServerInfoService as a request scoped object since it requires an HttpRequestBase. NOTE: That the basic web objects are already registered in the containers (such as HttpContextBase, HttpRequestBase, etc…)
public class MyUmbracoStartup : ApplicationEventHandler
{
protected override void ApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
//If you are using Autofac:
AutofacStartup.ContainerBuilding += (sender, args) =>
{
//add our own services
args.Builder.RegisterControllers(typeof(TestController).Assembly);
args.Builder.RegisterType().As().InstancePerRequest();
};
//If you are using LightInject:
LightInjectStartup.ContainerBuilding += (sender, args) =>
{
//add our own services
args.Container.RegisterControllers(typeof(TestController).Assembly);
args.Container.Register(new PerRequestLifeTime());
};
}
}
//service
public interface IServerInfoService
{
string GetValue();
}
//implementation of the service
public class ServerInfoService : IServerInfoService
{
private readonly HttpRequestBase _umbCtx;
//requires a request based object so this must be scoped to a request
public ServerInfoService(HttpRequestBase umbCtx)
{
_umbCtx = umbCtx;
}
public string GetValue()
{
var sb = new StringBuilder();
sb.AppendLine("Server info!").AppendLine();
foreach (var key in _umbCtx.ServerVariables.AllKeys)
{
sb.AppendLine($"{key} = {_umbCtx.ServerVariables[key]}");
}
return sb.ToString();
}
}
public class TestController : SurfaceController
{
private readonly IServerInfoService _serverInfoService;
public TestController(IServerInfoService serverInfoService, UmbracoContext umbCtx): base(umbCtx)
{
_serverInfoService = serverInfoService;
}
//see /umbraco/surface/test/index to see the result
public ActionResult Index()
{
return Content(_serverInfoService.GetValue(), "text/plain");
}
}
Happy holidays!