One of my favourite Dependency Injection (DI) containers is Jeremy Miller’s StructureMap, it’s full featured and light weight, but what I really like the most is the fluent configuration API which means we can write code like this:
ForRequestedType<INumber>()
.TheDefaultIsConcreteType<Number>();
I also like that you don’t need to put custom attributes in your code.
It’s all pretty abstract so lets assume we have a simple interface, I’m deliberately keeping it simple:
public interface INumber
{
int GetNumber();
}
A simple implementation of this interface:
public class Number : INumber
{
public int GetNumber()
{
return 999;
}
}
I’ve built a really simple (read: don’t use this in anything other than an experiment, the DI container in SharePoint gets configured for every request, including for images and scripts). This handler calls StructureMapConfiguration.AddRegistry with a class that derives from Registry which contains our fluent configuration information.
Why have I implemented this as a HTTPHandler?
If you read the Guidance on How to enable Unity in a SharePoint Application (unity is Microsoft’s DI container), the first step is to modify the Global.asax file. I have a problem with doing this for the simple reason that your touching the SharePoint system files, any upgrade will likely cause problems. It really is a shame that you can’t access the application onstart event using another mechanism.
I most certainly agree that the application onstart event is the perfect place to setup your DI container and if your happy to modify and deploy your changes to a global.asax file then go right ahead, put the code below into this file.
So it got me thinking, would a DI container like StructureMap work if it was setup in the begin request method of a HttpHandler? so my test code is:
namespace DIHttpModule
{
public class DIHttpModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e)
{
StructureMapConfiguration.AddRegistry(new DIServiceRegistry());
INumber numberGen = ObjectFactory.GetInstance<INumber>();
((HttpApplication)sender).Response.Write("Number: " + numberGen.GetNumber().ToString());
}
}
public class DIServiceRegistry : Registry
{
protected override void configure()
{
ForRequestedType<INumber>()
.TheDefaultIsConcreteType<Number>();
}
}
}
So after I deploy and configure my HttpHandler I do in fact see the number ‘999’ display at the top of each page (it also breaks JavaScript etc. because it writes this value into every request)
What has happened is that the call ObjectFactory.GetInstance<INumber>() has created an instance of Number(), we didn’t have to explicitly new up a Number() object.
That is a pretty basic example, lets expand it a little:
public class NumberPrinter
{
private INumber _number;
public NumberPrinter(INumber number)
{
_number = number;
}
public string PrintNumber()
{
return _number.GetNumber().ToString();
}
}
Notice the constructor, it takes an INumber interface, the constructor is special as far as StructureMap is concerned, if StructureMap is asked to create an instance of NumberPrinter it will see that the constructor takes an INumber parameter and will attempt to pass in an instance of that type.
So assuming we changed the HttpModule to the following code:
NumberPrinter printer = ObjectFactory.GetInstance<NumberPrinter>();
((HttpApplication)sender).Response.Write(" Number : " + printer.PrintNumber());
We can see that again our output is ‘999’, StuctureMap was smart enough to create an Instance of INumber and pass it in, if you think about it, that’s pretty cool. It means that you can replace any concrete Instance of INumber with a single configuration change, that is powerful when we want to test our object in isolation, which is a whole other topic.
That’s a pretty trivial example, we did the StructureMap configuration and called ObjectFactory.GetInstance in the same method, now lets try it out in a web part which will be invoked later in the page life cycle (also remove the response.write from the HttpModule):
public class NumberWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
{
NumberPrinter printer = ObjectFactory.GetInstance<NumberPrinter>();
writer.Write("Web Part: " + printer.PrintNumber());
}
}
Running this web part gives the expected result:
Again this is a really trivial example, that’s the point, you can clearly see that we are using a DI tool called StructureMap to create instances of our objects. Sure you could do away with the HttpHandler and move the configuration into the web.config file. But I do think the HttpHandler method warrants more thought however, because its useful when you introduce NHibernate to the mix, which is another framework that needs to do some expensive start up, which is also suited to the global.asax methods, but it does have some characteristics that should be managed per request, but that’s another post.
For now, hopefully I’ve proved some thought around DI and how can we manage the SharePoint deployments that really yell out for Application OnStart events without modifying the system file.