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.

12 Comments

  • Thank you for this more advanced example.
    I am working on a project that will use this. (Just working on the data access now with the Entity Framework)
    I have been trying to keep all and only data access code in a separate assembly and inject dependencies with Windsor. (No reference from the Core assembly to the Data assembly)
    I am not sure if what I am doing is right, but I extracted interfaces from the Designer generated classes and made the Partial Class declaration inherit from theses Interfaces.
    Then I have a Data access service injected by the IOC too that provide the Interfaces to the UI or test layer.
    I also have a Business service living in the Core assembly injected by the IOC into the Data assembly to apply business rules.
    Seems rather complex, it may be the price to pay for flexibility.

  • I hope the RequestContext class is mockable, since IControllerFactory takes a dependency on it for the CreateController method.

    I'm sure at some point in the future, we are going to want to build smart factories and will want to test them.


  • Evan:
    The RequestContext has two properties:
    HttpContext : IHttpContext
    RouteData : RouteData
    The RouteData holds information about the Route.
    When we want to write a test we use the interfaces shipped with the System.Web.Extensions namesapce. So we will have a dependency to the System.Web.Extensions in our test. Note: We still don't need to use ASP.Net to test our Controllers etc. When we write our test we could easy create an instance of the RequestContext and add the information we need to it, for example:
    &nbsp; IHttpContext httpContext = new MockHttpContext();
    &nbsp; //...
    &nbsp; RouteData routeData = new RouteData();
    &nbsp; routeData.Route = new Route("[contorller]/[action]/[id]", typeof(MockRouteHandler));
    &nbsp; RequestContext context = new RequestContext(httpContext, routeDate);
    &nbsp; IControllerFactory myFactory = new MyControllerFactory();
    &nbsp; IController controller = myFactory.CreateController(context, typeof(ProductController));
    Hope that make sence?

  • This is very nice. I hope a CTP is released soon.

    What are your feelings about MVC vs WebForms now? Are you starting to see the benefits of this MVC architecture?


  • Torkel:
    The ASP.Net MVC Framework rocks! ;)
    Well they have more to go until it will totally rock. If I want to use the normal Web Form with postback or MVC depends on the project and the needs. But I think I will mostly use the MVC Framework because of its testability etc.

  • cool, being that the drive is to get mvc tdd, mocking would be a requirement.

  • Hi,

    This is a great post and has helped tremendously (thank you).

    Is there any way to implement Session-Scoped objects? Right now when I try I get this error: 'session'-scoped objects require SessionState to be enabled.

    Of course setting has no effect.

    Ideas?

  • Does anyone know how often a asp.net mvc controller constructor is gets called? I assumed they would get created and destroyed with every request, but that does not seem to be the case.

  • A great resource - many thanks!

  • By default Spring creates objects as singletons. That means in the case above there would only be one instance of the HomeController that would be handed out by Spring for each request. One of the issues with this is the ViewData would not be reset/refreshed and data set in a previous request would be available for a following one, even if that request had originated from a different user. To get round this add the configuration option singleton="false" to the HomeController config and Spring will treat the object as a prototype and create a new instance for every request.




  • Can someone please clearly explain how to integrate Spring.Net into ASP.Net MVC

  • Nice, this was excatly what I was looking for to getting started with MVC and Spring

Comments have been disabled for this content.