We’ve all read about what MVC is, but how does it really work? What are the nuts and bolts that make that MVC magic happen? Fortunately Microsoft has released the source code to ASP.NET MVC and we can read the details in all their glory. But if you are like myself reading source code is something far different than truly understand how an application works. Luckily since the source code has been released we can do much more than read it, we can use the debugger to step through it.
The debugger in Visual Studio is an extremely helpful tool for the modern developer. I often use the debugger as my first debugging route for development code. Interestingly enough we can put that same debugger to use to walk through ASP.NET MVC to get a better feel for how it operates.
The first thing we need to do is download the source code to ASP.NET MVC and compile it with debugging enabled. The source code can be downloaded from it’s CodePlex page. The download contains a Visual Studio 2008 project. Simply open the project and do a rebuild. Once completed you should see four files in the bin directory located directory in the folder you extracted the downloaded source code into.
Of those files we are only interested in two of them. System.Web.Mvc.dll and System.Web.Mvc.pdb. Make note of where those files are located on your computer, because we will need to add a reference to those shortly.
In a new instance of Visual Studio lets create a new ASP.NET MVC application. The first thing we need to do is to delete the reference to System.Web.Mvc. Expand the references folder, and right-click System.Web.Mvc and select remove.
With the reference to System.Web.Mvc removed you will now need to add it back. Only this time instead of referencing the DLL that shipped with the ASP.NET MVC we will reference the DLL that we just compiled. By right clicking on the references folder and selecting “Add Reference…” you will be given the option to browse for the reference. Simply navigate to the path I asked you to remember earlier and select the System.Web.Mvc.dll file and click “OK”.
Now we are all ready to start debugging the innards of ASP.NET MVC. For starters navigate to the pre-generated HomeController and put a break point on the first line of the Index method. Now start the site with debugging enabled.
Once everything gets done compiling and the application starts your Visual Studio instance should immediately stop at your break point.
At this point in your Call Stack window you should see entries for System.Web.MVC.DLL, and those entries should be in black (not gray). This means that Visual Studio has the debugging information that it needs to show us source code for those methods. This is precisely what we were trying for, Visual Studio will now all us to step through the ASP.NET MVC code in a very similar fashion to our code. Lets start by analyzing the call stack in detail.
In the call stack we see the following:
MvcApplication1.DLL!MvcApplication1.Controllers.HomeController.Index() System.Web.Mvc.DLL!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(...) System.Web.Mvc.DLL!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters.AnonymousMethod(...) System.Web.Mvc.DLL!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(...) System.Web.Mvc.DLL!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters.AnonymousMethod() System.Web.Mvc.DLL!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(...) System.Web.Mvc.DLL!System.Web.Mvc.ControllerActionInvoker.InvokeAction(...) System.Web.Mvc.DLL!System.Web.Mvc.Controller.Execute(...) System.Web.Mvc.DLL!System.Web.Mvc.Controller.System.Web.Mvc.IController.Execute(...) System.Web.Mvc.DLL!System.Web.Mvc.MvcHandler.ProcessRequest(...) System.Web.Mvc.DLL!System.Web.Mvc.MvcHandler.ProcessRequest(...) System.Web.Mvc.DLL!System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(...)
At this point the call stack turns from black to gray and we also go from the System.Web.Mvc namespace to just the System.Web namespace. This is the transition point where ASP.NET MVC is executionally different from standard ASP.NET. Looking a little bit farther in the call stack we see the following entries:
System.Web.dll!System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() System.Web.dll!System.Web.HttpApplication.ExecuteStep(...) System.Web.dll!System.Web.HttpApplication.ApplicationStepManager.ResumeSteps(...) System.Web.dll!System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(...) System.Web.dll!System.Web.HttpRuntime.ProcessRequestInternal(...) System.Web.dll!System.Web.HttpRuntime.ProcessRequestNoDemand(...) System.Web.dll!System.Web.HttpRuntime.ProcessRequest(...)
Anyone familiar with how regular ASP.NET executes page request will surely recognize the call to IHttpAsyncHandler.BeginProcessRequest. This hints at the fact that ASP.NET MVC ties into the ASP.NET execute engine using a custom IHttpHandler. Sure enough the first stack trace in the System.Web.Mvc namespace seems to confirm this suspicion. The entry talks of a class called MvcHandler in System.Web.Mvc. Double clicking on that call stack entries takes Visual Studio straight to the source code.
The method is rather sparse, but this stands to reason considering its an explicit implementation of the IHttpHandler interface acting as a wrapper for their own protected method by the same name. Going one more level up in the call stack reveals this other method.
This particular ProcessRequest is wrapping the HttpContext so that it can call the more “generic” ProcessRequest method that will actually do the first bit of real work in the ASP.NET MVC execution stack.
In this third (and final) ProcessRequest method the ASP.NET MVC framework is, at a high level, processing the route table, finding a suitable controller and requesting the controller to execute. I say this is happening at a high level because, as you can see, the method itself delegates most of that work to other classes. The ControllerBuilder factory is fetched on line 53, and the execution of the controller is left entirely up to the controller’s Execute method. At this point we are about to leave the bulk of ASP.NET MVC behind and entire the Controller’s Execute pipeline. The twenty lines of code comprising the above ProcessRequest method is the heart and sole of ASP.NET MVC. This seems extremely streamlined when you compare that to what a regular ASP.NET request has to go through.
Obviously the guys who are creating ASP.NET MVC wrote a lot more than that one method. The entirely framework depends upon such things that we haven’t looked at like the ControllerFactory, the RequestContext, and even the Controller itself is a well though out piece of code. However all of those things (by design) can be overridden or completely replaced. We have already seen how people have plugged in custom view engines and even custom controller factories. This just illustrates that ASP.NET MVC’s power will truly come from it’s simplicity.