In my
last post, I showed how you could set up dependency injection with Windsor Container by using the Web.config file. We left off with this piece of code:
private void BindArticles()
{
var container = new WindsorContainer(new XmlInterpreter());
var articleService = container.Resolve<IArticleService>();
uxArticleView.DataSource = articleService.GetActive();
uxArticleView.DataBind();
}
While this code will perform the injection perfectly, it does make the mistake of initializing the Windsor Container in the method call. Since this is an expensive operation in terms of performance, I am going to show how I initialize the container in my web applications. Keep in mind, there are 100 ways to do this; this is the method I chose based on experiences while working with Windsor.
When designing a web application, I like to take Brad Abrams advice in his book “Framework Design Guidelines” and try to envision how this code will be used throughout the application. I would like it to be as seamless as possible with each page (perhaps by using a base class) and I would like to keep the dependency injection framework-agnostic so that if I choose to switch to StructureMap or some other dependency injection framework, my codebase isn’t tightly-bound to Windsor.
On that note, I’m going to show you my vision for using these interfaces throughout the web application. Here is a sample web page that will list the featured articles for a website:
public partial class FeaturedArticles : System.Web.UI.Page
{
public IArticleService ArticleService { get; set; }
protected override void OnInit(EventArgs e)
{
BindFeaturedArticles();
base.OnInit(e);
}
private void BindFeaturedArticles()
{
uxFeaturedArticles.DataSource = ArticleService.GetFeatured();
uxFeaturedArticles.DataBind();
}
}
To me, this code is easy to understand and clean. An article service simply returns articles from somewhere to my ‘uxFeaturedArticles’ ListView control and binds them. I would prefer to see the ArticleService as a private variable that is passed in through constructor injection - unfortunately, pages and user controls must have a default constructor with no parameters, so constructor injection isn’t a viable option (but it works great with MVC!). To make this work, I’ll need to write a base class that resolves the dependencies of each page automatically. The base class is extremely simple:
public class DIPage : System.Web.UI.Page
{
public DIPage()
{
DependencyResolver.Resolve(this);
}
}
This class will call a static method on a DependencyResolver class and pass in the instance of this page whenever DIPage is contructed. The DependencyResolver class will be responsible for finding and using the initialized Windsor container and passing back the resolved instance to the caller. Here is the first run at the DependencyResolver class:
public class DependencyResolver
{
private static IWindsorContainer _container;
public void Resolve<T>(T instance) where T : class
{
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
if (!property.PropertyType.IsInterface || property.PropertyType.IsGenericType)
{
continue;
}
property.SetValue(instance, _container.Resolve(property.PropertyType), null);
}
}
}
This class looks a bit complicated at first glance, but it is basically using reflection to find all public properties of the instance that is passed in. Next, it iterates each property and skips the ones that are not interfaces or are generic types (e.g. IList or IEnumerable). Finally, it sets the property’s value to a resolved instance of the property type (in this case, IArticleService). The downfall is that you can only register one instance of this type in the config at a time; however, if you want to register more, you could change this method to take a string “key” as a parameter and use that to look up different components.
You might also note that we haven’t set the _container variable to anything yet. The reason is that I don’t want to tightly-bind my application to Windsor, so I am going to rewrite this class to be more generic. In doing so, we can use any dependency injection framework to resolve. Here is the generic version of this class:
public class DependencyResolver
{
private static IDependencyResolver _resolver;
public static void InitializeWith(IDependencyResolver resolver)
{
_resolver = resolver;
}
public static void Resolve<T>(T instance) where T : class
{
_resolver.Resolve(instance);
}
}
Basically, I have rewritten this class as a wrapper for an interface (IDependencyResolver) which contains basically the same methods:
public interface IDependencyResolver
{
void Resolve<T>(T instance) where T : class;
List<Type> NotSupportedTypes { get; }
IDependencyResolver InitializeWith<T>(T container) where T : class;
}
This interface contains the same two methods used above as well as a property which I will cover in a moment. Notice that the DependencyResolver class above does not implement this interface. Instead it creates a wrapper around the interface calls, and calls them whenever a call to the DependencyResolver is made.
This interface will be the extensibility point for other DI frameworks. In the case of Windsor, it will look like this:
public class WindsorDependencyResolver : IDependencyResolver
{
private static IWindsorContainer _container;
public List<Type> NotSupportedTypes
{
get
{
return new List<Type>
{
typeof(IPrincipal),
typeof(IDictionary),
typeof(ISite),
typeof(ITemplate)
};
}
}
public IDependencyResolver InitializeWith<T>(T container) where T : class
{
if (!(container is IWindsorContainer))
{
throw new Exception("Container must inherit from IWindsorContainer when using the WindsorDependencyResolver.");
}
_container = (IWindsorContainer)container;
return this;
}
public void Resolve<T>(T instance) where T : class
{
if (_container == null)
{
throw new Exception("The container has not been properly initialized. Please call InitializeWith() on the service locator to initialize the container before attempting to resolve.");
}
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
if (!property.PropertyType.IsInterface || NotSupportedTypes.Contains(property.PropertyType) || property.PropertyType.IsGenericType)
{
continue;
}
property.SetValue(instance, _container.Resolve(property.PropertyType), null);
}
}
}
The Resolve
(T instance) method is almost the exact same implementation as I showed above; however, I have added in an additional check to ensure that each property on the page is not one of the NotSupportedTypes. I did this because I quickly found that there are some interface properties as part of the System.Web.UI.Page hierarchy, and it will evaluate them and throw an error as they are not defined in your web.config. Anytime you receive an error for a weird component like ITemplate, simply add it to the list above, and it will ignore it.
You may also notice that the _container variable is finally being initialized with the InitializeWith() method . This will allow us to pass in the container when initializing the web application – it will throw an error if anything else is passed in.
We’re almost there. Now that we have designed a flexible system for resolving dependencies, let’s write the code to initialize them. The global.asax Application_Start is probably the best place to do this, as this is run when your application starts and that’s it. Go ahead and add a global.asax file to your application and write the following code:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
var container = new WindsorContainer(new XmlInterpreter());
var resolver = new WindsorDependencyResolver();
DependencyResolver.InitializeWith(resolver.InitializeWith(container));
}
}
To summarize, this code will load and initialize the Windsor Container from the web.config file, initialize a WindsorDependencyResolver, and then pass the container with the resolver into our static DependencyResolver class. If you wanted, you could register the WindsorDependencyResolver in your web.config as an implementation of IDependencyResolver and swap that out if needed.
To make it all work, all we need to do is change the inherited class to our DIPage:
public partial class FeaturedArticles : DIPage
{
public IArticleService ArticleService { get; set; }
protected override void OnInit(EventArgs e)
{
BindFeaturedArticles();
base.OnInit(e);
}
private void BindFeaturedArticles()
{
uxFeaturedArticles.DataSource = ArticleService.GetFeatured();
uxFeaturedArticles.DataBind();
}
}
From here on out, simply have your pages inherit from DIPage and then create public properties for each of the services you would like to use on the page. The base class will automatically resolve them after they are registered in the web.config!
I’ve run some performance tests, and it works extremely fast. My only concern is that maybe I have opened the door to memory leaks, but I will let someone else point that out if I have indeed overlooked something. <g> Please let me know if I can clarify anything further.