In two previous articles (
here and
here), I showed how we can refactor static service calls into interfaces and constructor injection. We left off with this bit of code:
public class ArticleService : IArticleService
{
private readonly IArticleRepository _articleRepository;
public ArticleService(IArticleRepository articleRepository)
{
_articleRepository = articleRepository;
}
public IList<Article> GetActive()
{
var articles = _articleRepository.Get();
return articles.Where(article => article.IsActive).ToList();
}
}
Where the calling class looked like this:
private void BindArticles()
{
IArticleService articleService = new ArticleService(new ArticleXmlRepository());
uxArticleView.DataSource = articleService.GetActive();
uxArticleView.DataBind();
}
As you can see, an instance of the IArticleRepository interface is created and then passed into an instance of IArticleService. This is not ideal as it doesn’t allow us the opportunity to swap out implementations without recompiling the entire application.
Enter dependency injection.
If you would like to follow along, you will need to download the latest release candidate from
castleproject.org, which you can find
here.
Once you’ve installed it, we’ll need to add references to the proper Castle libraries. The installation will place all binaries into the global assembly cache so that you can reference them directly without having to hunt down the folder. For this demo, we will just add references to the following DLLs:
- Castle.Core
- Castle.MicroKernel
- Castle.Windsor
Once this is complete, we can get started. The Windsor Container is a class that holds information about your object graph, dependencies, and more advanced topics. Let’s begin by configuring Windsor in the web.config file. You’ll need to add the following configSection for Windsor to work.
<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
Now we can begin registering our components. The basic layout for the sake of this demo is as follows:
<castle>
<components>
<component id="key" service="serviceType" type="classType">
</component>
</components>
</castle>
The
key is a name that you can make up so that you can reference this component programmatically and throughout the config file. The
serviceType is the full namespace and assembly of the interface we wish to register and the
classType is the full namespace and assembly of the implementation of our interface. Let’s register our article service class (where LowryMedia.Web is the assembly name):
<castle>
<components>
<component id="article.service"
service="LowryMedia.Web.Models.IArticleService, LowryMedia.Web"
type="LowryMedia.Web.Models.ArticleService, LowryMedia.Web">
</component>
</components>
</castle>
There, that was easy. We also need to register the two repositories… ArticleXmlRepository and ArticleSqlRepository:
<castle>
<components>
<component id="article.service"
service="LowryMedia.Web.Models.IArticleService, LowryMedia.Web"
type="LowryMedia.Web.Models.ArticleService, LowryMedia.Web">
</component>
<component id="article.sql.repository"
service="LowryMedia.Web.Models.IArticleRepository, LowryMedia.Web"
type="LowryMedia.Web.Models.ArticleSqlRepository, LowryMedia.Web">
</component>
<component id="article.xml.repository"
service="LowryMedia.Web.Models. IArticleRepository, LowryMedia.Web"
type="LowryMedia.Web.Models.ArticleXmlRepository, LowryMedia.Web">
</component>
</components>
</castle>
Now that everything is registered, we get to the fun part. Remember the constructor we created for the ArticleService?
public ArticleService(IArticleRepository articleRepository)
{
_articleRepository = articleRepository;
}
We can also register that in our configuration. Let’s say we want to begin by using the Sql implementation of the repository. Here is the configuration for that… notice that the parameter name matches the parameter name from above (articleRepository).
<components>
<component id="article.service"
service="LowryMedia.Web.Models.IArticleService, LowryMedia.Web"
type="LowryMedia.Web.Models.ArticleService, LowryMedia.Web">
<parameters>
<articleRepository>${article.sql.repository}<articleRepository>
</parameters>
</component>
<component id="article.sql.repository"
service="LowryMedia.Web.Models.IArticleRepository, LowryMedia.Web"
type="LowryMedia.Web.Models.ArticleSqlRepository, LowryMedia.Web">
</component>
<component id="article.xml.repository"
service="LowryMedia.Web.Models. IArticleRepository, LowryMedia.Web"
type="LowryMedia.Web.Models.ArticleXmlRepository, LowryMedia.Web">
</component>
</components>
</castle>
Notice the ${article.sql.repository} inside the articleRepository tags. This is the notation for referencing other components in the configuration. This basically tells the Windsor container that when the article service is loaded, to pass in an instance of the ArticleSqlRepository to the service’s constructor.
Let me show you how the code will look from our calling class. For those not using
ReSharper (which I
highly recommend), I will include the proper using statements at the top.
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
private void BindArticles()
{
var container = new WindsorContainer(new XmlInterpreter());
var articleService = container.Resolve<IArticleService>();
uxArticleView.DataSource = articleService.GetActive();
uxArticleView.DataBind();
}
That’s it! Now our article service will be called with the Sql implementation. To change this to xml, all you need to do is swap out the configuration above with ${article.xml.repository}.
A few things to note about the above code. First, the XmlInterpreter tells Windsor to grab the configuration from our .config file. There are other configuration stores to use, but that is outside the scope of this article. Second, and most importantly, while this code will work fine, it is far from ideal. The container should only be instantiated once in our whole application, and we wouldn’t want to use a method-scoped dependency resolution like this… it would probably be more useful as a class-variable, but I will get into this in my next article when I discuss setting up Windsor and dependency injection for a .Net Web Application (not MVC).