-->

29/10/2011

Default Controller Factory Vs Custom Controller Factory - MVC Razor


In order to have a better understanding of this, lets start this discussion with MVC Page life cycle.
Let's see step by step how it works.



#1. URLRoutingModule : 
If you open the Global.asax.cs file, you can see the below mentioned code.
public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }
This is nothing but the Routing module we are talking. So, in MVC Page control life cycle, the request will hit Routing module first. Duty of routing module is to segregate the URL and identify which is controller, which is the action method and what is the parameter.
Now, this information will be sent to Route handler.

#2. MVC Route Handler: 
This is the route handler provided by the MVC framework. As you saw in the January column, it is the route handler's job to find an HTTP handler for a request—that is an object implementing the IHttpHandler interface. In MVC applications, this object will be an object of type MvcHandler, and inside MvcHandler is where processing becomes interesting.

#3. MvcHandler :
When control reaches the MvcHandler, the MvcHandler is able to extract the controller parameter from the RouteData produced by the routing module earlier in the processing. The handler will ultimately send this controller parameter, which is a string value, to a controller factory. It is then the factory's responsibility to construct and return a controller. All controllers in an MVC application implement the IController interface.

#4. IControllerFactory : 
The MVC framework provides a default controller factory (aptly named DefaultControllerFactory) that will search through all the assemblies in an appdomain looking for all types that implement IController and whose name ends with "Controller." Thus, if you tell the factory to look for a "Home" controller, the factory can return a newly instantiated instance of a HomeController class regardless of the namespace or assembly it lives in—as long as it implements IController.

#5. IController :
Once the MvcHandler has an IController reference from the factory, it invokes Execute on the controller and waits for the controller to work its magic. When execution is complete, the MvcHandler will check if the controller implements the IDisposable interface, and if so, will invoke Dispose on the controller to clean up unmanaged resources.

By this we have done with Page control life cycle of MVC.
Great, everything went well, then what is the problem?

Problem : Lets say i want to pass on a comment from user to controller and thus use it further in operations defined in Home controller.
So, best way to do is Dependency Injection (IoC : Constructor DI).
So i modified created a constructor for the home controller with a parameter say, string Comment.
namespace MVCTestDefaultControllerFactory.Controllers
{
    public class HomeController : Controller
    {
        string strComment;
        public HomeController(string comment)
        {
            strComment = comment;
        }
        public ActionResult Index()
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }
}
Wonderful, now lets see by hitting the url , which we have been using from beginning of MVC lessons.

"http://localhost:40708/Home/Index"
Out put:
Woow, wait a second, we haven't encountered this issue before. What we might have done wrong.

Root Cause:
In the above discussion, we have mentioned that MVC by default uses, "IControllerFactory" method to invoke pertaining controller based on controller name given by Route Handler.

So, IControllerfactory is a asset of framework, Although the default factory provided by the framework can find a HomeController anywhere in your solution, it can only instantiate the HomeController if you provide a "parameterless constructor". This limitation is a problem for teams who follow the dependency inversion principle and inject a controller's dependencies via its constructor.

Hope you got the problem now.
Solution is using a Custom Controller Factory with StructureMap.
We will see how to do it in next post in detail.

Is it helpful for you? Kindly let me know your comments / Questions.

3 comments:

  1. I prefer using Ninject rather than StructureMap. Would you consider doing the next column on DI/IoC with Ninject as well?

    Also an aspiring architect :)

    ReplyDelete
  2. Interesting article Pratap.
    Waiting for next version. BTW, is it possible to remove/rename suffix 'Controller' for controller class names?

    ReplyDelete