In an earlier blog I introduced the idea of using ASP.NET MVC to develop DotNetNuke modules, and I walked through how you could create a simple ASP.NET MVC Module Application.  Now that I have demonstrated that it is feasible to use the MVC Framework in a WebForms Application, I will start to dig into how this was done.

But first, before we look at the code, lets review how the standard ASP.NET MVC Pipeline works. This is summarized effectively by Steve Sanderson, on his blog.

Figure 1 – ASP.NET MVC Pipeline

This diagram was based on an early Preview of the ASP.NET MVC Framework, but the basic process has not changed.  There are 4 stages in the standard ASP.NET MVC pipeline; Routing, Instantiate Controller, Locate and Execute Action, Instantiate and Render View.

Routing

In the routing step the Incoming Http Request is mapped to the MvcHandler and the MvcHandler’s ProcessRequest method is called, assuming of course that you have registered the handler in your web.config file (Listing 1).

Listing 1 – Registering the MVCHttpHandler in web.config
<add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" 
type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35"
/>


Controller Instantiation

The ProcessRequest method (Listing 2) creates a ControllerFactory (line 8), which in term attempts to create a Controller (line 9), using the RouteData  which is part of the RequestContext.  Assuming that the ControllerFactory is able to create a Controller Instance, ProcessRequest will call the Execute method of the Controller (line 19), thus passing control to the Controller.

Listing 2 – MvcHandler, ProcessRequest method
   1:  protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
   2:      AddVersionHeader(httpContext);
   3:   
   4:      // Get the controller type
   5:      string controllerName = RequestContext.RouteData.GetRequiredString("controller");
   6:   
   7:      // Instantiate the controller and call Execute
   8:      IControllerFactory factory = ControllerBuilder.GetControllerFactory();
   9:      IController controller = factory.CreateController(RequestContext, controllerName);
  10:      if (controller == null) {
  11:          throw new InvalidOperationException(
  12:              String.Format(
  13:                  CultureInfo.CurrentUICulture,
  14:                  MvcResources.ControllerBuilder_FactoryReturnedNull,
  15:                  factory.GetType(),
  16:                  controllerName));
  17:      }
  18:      try {
  19:          controller.Execute(RequestContext);
  20:      }
  21:      finally {
  22:          factory.ReleaseController(controller);
  23:      }
  24:  }


Action Invoker

We have now successfully routed control to the appropriate Controller. 

The Execute method of the ControllerBase class calls the ExecuteCore (Listing 3) method of the Controller class.  This in turn calls the ActionInvoker (line 6), which again uses the RouteData to invoke the specific action on the Controller. 

Listing 3 – Controller, ExecuteCore method
   1:  protected override void ExecuteCore() {
   2:      TempData.Load(ControllerContext, TempDataProvider);
   3:   
   4:      try {
   5:          string actionName = RouteData.GetRequiredString("action");
   6:          if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
   7:              HandleUnknownAction(actionName);
   8:          }
   9:      }
  10:      finally {
  11:          TempData.Save(ControllerContext, TempDataProvider);
  12:      }
  13:  }

An action maps to a method name in the Controller class.

Listing 4 – The HomeController class
   1:  public class HomeController : Controller
   2:  {
   3:      public ActionResult Index()
   4:      {
   5:          ViewData["Message"] = "Welcome to ASP.NET MVC!";
   6:   
   7:          return View();
   8:      }
   9:   
  10:      public ActionResult About()
  11:      {
  12:          return View();
  13:      }
  14:  }

For example in Listing 4 which shows the HomeController in the default ASP.NET MVC project – the Index and About methods represent the index and about actions of the HomeController. 

You would reach these methods using the routes “/home/index” and “/home/about”, although as the first route represents the default controller “home” and the default action “index”, the null route would also reach the Index method.

Render View

Finally the ActionInvoker gets the returned ActionResult from the action method ands calls its ExecuteResult method.  If the ActionResult is a ViewResult (Listing 5) this in turn will render the View (line 17).

Listing 5 – ViewResultBase, ExecuteResult
   1:  public override void ExecuteResult(ControllerContext context) {
   2:      if (context == null) {
   3:          throw new ArgumentNullException("context");
   4:      }
   5:      if (String.IsNullOrEmpty(ViewName)) {
   6:          ViewName = context.RouteData.GetRequiredString("action");
   7:      }
   8:   
   9:      ViewEngineResult result = null;
  10:   
  11:      if (View == null) {
  12:          result = FindView(context);
  13:          View = result.View;
  14:      }
  15:   
  16:      ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);
  17:      View.Render(viewContext, context.HttpContext.Response.Output);
  18:   
  19:      if (result != null) {
  20:          result.ViewEngine.ReleaseView(context, View);
  21:      }
  22:  }


Plugging in to the ASP.NET MVC Pipeline

So, given that DotNetNuke is a WebForms Application how do we plug in to this Pipeline with the least possible impact .  What I mean by this is that we don’t want to have to rewrite the MVC Framework in order to get everything to work.  We want to be able to use all of the extensibility points that the Framework enables, with a minimum amount of effort.

In the next part of this series I will describe how we achieve this goal.


Posted in: DotNetNuke  Tags: , , ,

 Search Blog

 Adsense

 Calendar

«  January 2010  »
MoTuWeThFrSaSu
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567
View posts in large calendar
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010 Thoughts from the Wet Coast