ASP.Net MVC Framework - Create your own IControllerFactory and use Spring.Net for DI
In this post I’m going to show you how we can easy create our own IControllerFactory for the ASP.Net MVC Framework and use Spring.Net to create our Controllers. I will in this post also use the Spring.Net Dependency Injection support to pass a Repository that should be used by our Controller.
To create a ControllerFactory we need to implement the IControllerFactory interface. This interface has one method, CreateController. This method takes two arguments a RequestContext and the Type of the controller to create. The MVC Framework will locate our Controller and passed in the Type to our IControllerFactory. This is something I don’t like; I want to be responsible to look up my own Controller in my way, not let the MVC Framework locate the type in their way, It will only creates some magic. If we want to change this behavior in the current preview of the MVC Framework we can create our own RouteHandler, but this is out of topic in this post. When we use Spring.Net to get an instance of an object we use a string, so we can use the Name property or the Type passed as an argument to our CreateController method to get the name of the Controller.
Here is the implementation of an IControllerFactory which will use Spring.Net to instantiate a Controller:
public class SpringControllerFactory : IControllerFactory
{
public IController CreateController(RequestContext context, Type controllerType)
{
IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("objects.xml"));
IObjectFactory factory = new XmlObjectFactory(input);
return (IController)factory.GetObject(controllerType.Name);
}
}
In this code I use the XmlObjectFactory to create objects out from a specified XML file, in this case the "objects.xml" file. Within the objects.xml file we can specify the objects we want to be instantiated when we are using the Spring.Net framework. I will later in this post show the content of the "objects.xml" file. But first we can take a look at the implementation of a Controller.
public class HomeController : Controller
{
IHomeRepository _homeRepository;
public HomeController() : this(new HomeRepository()) {}
public HomeController(IHomeRepository homeRepository)
{
this._homeRepository = homeRepository;
}
[ControllerAction]
public void Index()
{
CompanyInfo companyInfo = this._homeRepository.GetCompanyInfo();
RenderView("Index", companyInfo);
}
[ControllerAction]
public void Contact()
{
CompanyInfo companyInfo = this._homeRepository.GetContact();
RenderView("Contact", companyInfo);
}
[ControllerAction]
public void About()
{
CompanyInfo companyInfo = this._homeRepository.GetCompanyInfo();
RenderView("About", companyInfo);
}
}
I decided to modify the HomeController created by the "MVC Web Application" template. My modification of the HomeController was to remove the code to fill a CompanyInfo object, and instead put it into a Repository with the name "HomeRepostory".
public class HomeRepository : IHomeRepository
{
public CompanyInfo GetCompanyInfo()
{
CompanyInfo companyInfo = new CompanyInfo();
companyInfo.CompanyName = "Your company name here";
return companyInfo;
}
public CompanyInfo GetContact()
{
CompanyInfo companyInfo = new CompanyInfo();
companyInfo.CompanyName = "Your company name here";
companyInfo.AddressLine1 = "Company address Line 1";
companyInfo.AddressLine2 = "Company address Line 2";
companyInfo.City = "City";
companyInfo.State = "State";
companyInfo.Zip = "00000";
companyInfo.Email = "email@yourcompany.com";
return companyInfo;
}
}
The HomeRepository implements an interface with the name IHomeRepository. I made this design decision so I can easy mock my repositories. If we take a look at the HomeController again we can see that I have added a private field with the type IHomeRepository and also created a constructor which will take an IHomeRepository as an argument. The reason to this design is because of testability. For example if I mock the HomeRepository I can in my Test project easy inject the mock object for the Repository to my Controller.
MockHomeRepository mockHomeRepository = new MockHomeRepository();
HomeController homeController = new HomeController(mockRepository);
homeController.About();
Assert.AreEqual(((CompanyInfo)homeController.ViewData).companyInfo, "Your company name here");
By using a constructor which will take our IHomeRepository as an argument, we can also do a Constructor Injection by using the Sprin.Net framework, and that is exactly what I’m going to do in this post.
Now when we have our Controller and Repository we need to setup the “objects.xml” file with our objects and also setup a Constructor Injection. Here is the content of the “objects.xml” file:
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
<object id="HomeController" type="MvcApplication.Controllers.HomeController, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<constructor-arg name="homeRepository" ref="HomeRepository"/>
</object>
<object id="HomeRepository" type="MvcApplication.Models.Repositories.HomeRepository, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</objects>
The Object element specifies our objects we want to create with the Spring.Net framework. The id attribute specifies the name of the object and the type specifies the type of our object. When we use the Spring.Net framework’s GetObject method we pass in the name of the object to create, the GetObject method will look for an object with the id attribute set to the name, and instantiate the specified type. By using the <constructor-arg> element we can specify what object that should be passed into our constructor when we create the object. In this case I have specified the HomeRepository object.
When our IControllerFactory now will create our HomeController, Spring.net will create an instance of our Controller and do a constructor injection and pass in the HomeRepository object to the constructor and return our controller.
Now when we are done with all the implementation of our IControllerFactory, Controller and Repository we need to make sure our IControllerFactory should be used. This can be done in the Global.asax’s Application_Start event by using the SetDefaultControllerFactory method:
protected void Application_Start(object sender, EventArgs e)
{
ControllerBuilder.Current.SetDefaultControllerFactory(
typeof(MvcApplication.Models.Infrastructure.SpringControllerFactory));
...
}
When we now run our application, our IControllerFactory will be used and all the creating of our Controllers will be handled by the Spring.Net framework.