ASP.NET vNext Dependency Injection with Castle Windsor
In a previous post I explained how to set up and use the built in dependency injection container in ASP.NET vNext. Today we’re going to look at setting up and using the popular Castle Windsor DI container.
In order to use third party DI containers the MVC team set up a conforming container that the various DI containers tap into. Now, some will say conforming containers are an anti-pattern, and I lean on that side of the fence since they involve a lot of boiler plate code and limit the usefulness of your DI container. But this is the route they took and they have their reasons. It sounds like the MVC team is rethinking this strategy, though, and chances are the adapter code will live in the DI container projects themselves rather than in the DI repository. Hey, vNext is super-alpha so they’re allowed to make tons of changes and do whatever they want.
Regardless, as a personal experiment I wrote a Castle Windsor populator (I’m going to use the term populator here for lack of a better term). The ASP.NET team had previously written the Ninject and Autofac populators but have not gotten to the Castle Windsor one. My fork with the code is available here: https://github.com/davezych/DependencyInjection (I’m currently in preliminary talks with the ASP.NET team to open a pull request for it).
For this example we’ll use the sample MVC project available in the MVC repository. That already has AutoFac set up and we’ll modify it to instead use Castle Windsor. All of the code for this post is available in my fork of the MVC repository.
How do we use the new Windsor populator?
The Windsor populator I wrote follows the same patterns as the Ninject and Autofac ones that the ASP.NET team wrote. You instantiate a container, send it the services to populate and then return an IServiceProvider
that gets passed to the framework. When the framework needs an object, it uses the IServiceProvider
you give it. The code essentially boils down to:
var services = new ServiceCollection();
services.AddTransient<ITestService, TestService>();
var container = new WindsorContainer();
WindsorRegistration.Populate(container, services, app.ApplicationServices);
var sp = container.Resolve<IServiceProvider>();
app.UseServices(sp);
Setting up the Windsor populator in the ASP.NET MVC Sample
To get started, we first need to add a dependency to the Windsor DI package. We need to modify the project.json
file to add the dependency for Microsoft.Framework.DependencyInjection.Windsor
"net45": {
"dependencies": {
"System.ComponentModel.DataAnnotations": "",
"Microsoft.Framework.DependencyInjection.Autofac": "1.0.0-*",
"Microsoft.Framework.DependencyInjection.Windsor": "1.0.0-*",
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
"Autofac": "3.3.0"
}
}
If you’re using the Visual Studio 2014 CTP once you save the project.json
file it will automatically restore the dependencies. If you’re using the command line, open it up and run:
kpm restore
This will force the K Package Manager to restore all dependencies. The dependencies are placed by default in C:\Users\yourusername\.kpm\packages
. If you navigate to that folder, you should see a Microsoft.Framework.DependencyInjection.Windsor
folder and inside there a folder named 1.0.0-alpha3-something
(where something is the latest build number. It is currently 10148 at time of writing).
Now, since the Castle Windsor populator I created isn’t currently in the DI repo, we’ll have to build it from source and update the referenced package to use our custom copy of the library. We can do this by grabbing the compiled binaries and pasting them into the packages
folder.
First, though, update the project.json
file in the MVC sample project to reference the exact version of the Windsor DI library that we currently have (which, for me, is 1.0.0-alpha3-10148) instead of using the latest (aka *):
"net45": {
"dependencies": {
"System.ComponentModel.DataAnnotations": "",
"Microsoft.Framework.DependencyInjection.Autofac": "1.0.0-*",
"Microsoft.Framework.DependencyInjection.Windsor": "1.0.0-alpha3-10148",
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
"Autofac": "3.3.0"
}
}
This is necessary just in case a new version of the library is released as we’re working and it overwrites our version when we run kpm restore
.*
Next, navigate over to my fork and clone the repository: https://github.com/davezych/DependencyInjection. Navigate into the Windsor
folder and run:
kpm build
This will compile the project and place the binaries in the bin\Debug\net45
directory. Copy those compiled binaries and paste them into the Microsoft.Framework.DependencyInjection.Windsor\1.0.0-alpha3-10148
folder in your packages folder.
Back at the command line, navigate to the MVC sample folder and run:
k web
This will compile the project and start the development server. If you navigate to http://localhost:5001 you should see a test page with a bunch of junk on it (junk is good). At this point in time it is still utilizing Autofac. We’ll now edit the code to use the Windsor populator. First let’s expand the if statement that is checking the configuration and the DI system into 2 ifs:
if (configuration.TryGet("DependencyInjection", out diSystem))
{
if (diSystem.Equals("AutoFac", StringComparison.OrdinalIgnoreCase))
{
//Stuff
}
}
Next, pull out the shared code that instantiates the service collection:
if (configuration.TryGet("DependencyInjection", out diSystem))
{
var services = new ServiceCollection();
var defaultServices = Microsoft.AspNet.Hosting.HostingServices.GetDefaultServices();
foreach (var defaultService in defaultServices)
{
services.Add(defaultService);
}
services.AddMvc();
services.AddSingleton<PassThroughAttribute>();
services.AddSingleton<UserNameService>();
services.AddTransient<ITestService, TestService>();
services.Add(OptionsServices.GetDefaultServices());
if (diSystem.Equals("AutoFac", StringComparison.OrdinalIgnoreCase))
{
//Stuff
}
}
Finally, add an else if
that checks if the DI system is Windsor:
else if(diSystem.Equals("Windsor", StringComparison.OrdinalIgnoreCase))
{
var container = new WindsorContainer();
WindsorRegistration.Populate(container, services, app.ApplicationServices);
var sp = container.Resolve<IServiceProvider>();
container.BeginScope();
app.UseServices(sp);
}
For extra points, we’ll add an else
that throws an exception if a DI system is specified that we don’t know about:
else
{
throw new ArgumentException("Unknown dependency injection container: " + diSystem);
}
Now if you run k web
and navigate to http://localhost:5001 you should see that junk again. Hooray! We’re using Windsor – wait, what? No, wait, this is still Autofac. We still have to update the configuration! Open config.json
and swap Windsor in for Autofac:
{
"DependencyInjection": "Windsor"
}
Now run k web
and navigate to http://localhost:5001 and you should see junk again and everything should look the same but it’s using Castle Windsor this time! Whoooo!
Next time, I’ll explain how I built the Windsor populator and why I used some of the features I did.
- Ironically, this happened to me as I was writing the Castle Windsor populator and took me 20 minutes to figure out why
Microsoft.Framework.DependencyInjection.Windsor
was not a valid namespace. Thanks Fowler.