A year or so ago I wrote a series of blog posts about building testable modules using the Model-View-Presenter (MVP) pattern.  The MVP pattern of development provides many of the benefits attributed to the Model-View-Controller (MVC) pattern, which has become very popular recently as an alternative approach to ASP.NET development. 

You can have your cake and eat it too.

Its main strength though is that it retains most of the benefits of the WebForms Framework of rich controls, with which we are familiar. 

The MVP pattern achieves this by requiring the View (usually a UserControl or Page)which receives control from the WebForms Framework to pass “control” to a Presenter class, and it requires a good deal of discipline from developers to ensure that the pattern is followed. 

The pattern also requires developers to “wire-up” the three classes View, Presenter and Model (or ViewModel) every time they need a new control (or View).

This degree of complexity has tended to hamper the use of the MVP pattern, as it requires much more work on the part of developers in general (and module developers in particular).

I am pleased to announce that in the next public release of DNN (version 5.3) we will be including a set of base classes that provide most of this wiring-up out-of-the-box. 

This new framework is based on a fairly new Codeplex project WebFormsMVP from Tatham Oddie (of Readify in Australia) and Damian Edwards (formerly of Readify, but now assimilated by the Borg (Microsoft)).  Many of you will know that Phillip Beadle a long-time DNN Community member and now DNNCorp Employee also used to work at Readify.

We had already decided to add MVP support for module development in this development cycle and after meeting with Damian and Tatham at the recent MVP (as in Microsoft Most Valuable Professional) summit in Redmond, we decided to replace our MVP (Model-View-Presenter) code with their Library, which was much more advanced, and was licensed under a compatible Open Source license.  Tatham and Damian have used the WebFormsMVP framework on a number of high-profile websites in Australia so it is well-tested

Move over PortalModuleBase – Hello ModulePresenter and ModuleView<Model>

In order to take as much advantage as we can from this new framework we have built a thin layer on top of their code to ensure that the Presenters and Views all know about Module Context and Portal Settings. 

Two new modules that we will be delivering as part of the 5.3 release have been built on this new framework and will showcase how to use the framework and how to write tests at every layer of the application.

In addition, I will blog in future article(s) on how you can use this new framework to create testable modules of your own.


Posted in: DotNetNuke  Tags: , , ,

In my spare time, I often find myself playing with ideas for enhancing the DotNetNuke (DNN) core.  Most of the time I am developing prototypes as a “proof of concept” – i.e. they are not complete, and may never be completed.

One example of this is my recent blog series on developing modules for DNN using ASP.NET MVC.  So far I have demonstrated that the default MVC Application created using the ASP.NET MVC version 1 template can be converted, with little effort, into a DNN module.  In order to accomplish this I had to create a framework that sits on top of the DNN core.

The code to do this is very rough, there is still a lot of work to do, but I have had a number of requests to make the current code available. Where should it go?  I could include a zip file attached to my blog, but that is not very discoverable.  I could create a  DNN Forge project, but many of these ideas may never go anywhere, and some may end up being moved into the core.

We discussed this in the DotNetNuke Corp. Engineering group, and we have come up with the idea of a special DNN Forge project – called “DotNetNuke Labs”, where all the prototype ideas generated by members of the Engineering team, as well as Community Team members can be placed in one easily discoverable location.

I am pleased to announce today that "DotNetNuke Labs” is now live and the “DotNetNuke MVC Module Application Framework” project is the first sub-project to be added to this new Forge project.


Posted in: DotNetNuke  Tags: , , ,

In earlier blogs (Part 1 and Part 2 of this series) I described how I have developed a Framework that allows developers to create DotNetNuke (DNN) modules using the new ASP.NET MVC Framework.  In this blog I will describe the new base class which is used to enable this ability - MvcModuleApplication.

Prior to DotNetNuke 5.0, all module controls had to be ASP.NET User Controls that inherited from PortalModuleBase – a base class in the DotNetNuke Web Application Framework that provided the context necessary for DotNetNuke’s Module Injection logic to load and inject the module control in the page. (Since about DNN 4.4 a module control was not required to be an ascx file – it could be a compiled server control, but it still had to inherit from PortalModuleBase and thus ultimately from UserControl).

IModuleControl

In DNN 5.0 an interface was introduced – IModuleControl – and the Module Injection logic was refactored to require the control to implement IModuleControl, rather than inherit from PortalModuleBase.  PortalModuleBase itself was refactored to implement this interface so no existing modules were broken.

Creation of this interface means that it is no longer necessary to inherit from UserControl – although we are still required to ultimately inherit from the base class Control.

MvcModuleApplication

As demonstrated in Part 1 of this series, MVC Modules are required to include a class that inherits from MvcModuleApplication.

Listing 1 – The MVC_TestApplication class
   1:  public class MVC_TestApplication : MvcModuleApplication
   2:  {
   3:      protected override string FolderPath
   4:      {
   5:          get { return "MVC_Test"; }
   6:      }
   7:   
   8:      protected override void Init()
   9:      {
  10:          base.Init();
  11:          RegisterRoutes(Routes);
  12:      }
  13:   
  14:      private static void RegisterRoutes(RouteCollection routes)
  15:      {
  16:          routes.RegisterDefaultRoute("MVC_Test.Controllers");
  17:      }
  18:  }

Listing 1 shows the class we added to the Test MVC module we created in the first part of this series.  As discussed above, DotNetNuke Modules need to implement the interface IModuleControl and inherit from Control (this is necessary as the framework either needs to load a UserControl (ie ascx) or instantiate a Control and add it to the Controls collection of the Container.  This is demonstrated in Listing 2, which shows the new base class – MvcModuleApplication - before we add any  MVC Framework enabling code.

Listing 2 – The MvcModuleApplication class
   1:  namespace DotNetNuke.Web.Mvc
   2:  {
   3:      public abstract class MvcModuleApplication : Control, IModuleControl
   4:      {
   5:          #region IModuleControl Implementation
   6:   
   7:          public Control Control
   8:          {
   9:              get { throw new NotImplementedException(); }
  10:          }
  11:   
  12:          public string ControlPath
  13:          {
  14:              get { throw new NotImplementedException(); }
  15:          }
  16:   
  17:          public string ControlName
  18:          {
  19:              get { throw new NotImplementedException(); }
  20:          }
  21:   
  22:          public string LocalResourceFile
  23:          {
  24:              get { throw new NotImplementedException(); }
  25:              set { throw new NotImplementedException(); }
  26:          }
  27:   
  28:          public ModuleInstanceContext ModuleContext
  29:          {
  30:              get
  31:              {
  32:                  if (_moduleContext == null)
  33:                  {
  34:                      _moduleContext = new ModuleInstanceContext(this);
  35:                  }
  36:                  return _moduleContext;
  37:              }
  38:          }
  39:   
  40:          #endregion
  41:      }
  42:  }

Now that we have our base class, we can add the pieces to enable MVC modules.  In Part 2 of this series I described the MVC Pipeline.  We are going to plug in to this pipeline right at the beginning by implement logic similar to the ProcessRequest method of the MvcHandler. (So readers don’t have to refer back to the earlier blog, this method is shown in Listing 3).

Listing 3 - 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:  }

As the MvcModuleApplication class inherits from Control it is part of the WebForms Page Life Cycle.  We will override the OnInit method which is our first opportunity to implement our own code (Listing 4).

Listing 4 – MvcModuleApplication, OnInit
   1:  protected override void OnInit(EventArgs e)
   2:  {
   3:      base.OnInit(e);
   4:   
   5:      //Wrap the http Context
   6:      HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);
   7:   
   8:      // Setup the module's context
   9:      ModuleRequestContext moduleRequestContext = new ModuleRequestContext
  10:      {
  11:          ModuleContext = ModuleContext,
  12:          ModuleRoutingUrl = "", // for now we will pass in an empty string
  13:          HttpContext = httpContext
  14:      };
  15:   
  16:      _moduleResult = ExecuteRequest(moduleRequestContext);
  17:  }

In this method we first create an instance of HttpContextBase – from the HttpContext (line 6).  The MVC Framework uses this abstract base class to enable better testability, so we need to do the same. 

Next, we create a ModuleRequestContext object (lines 9-14).  This class is a helper class that enables us to pass around the ModuleContext (from IModuleControl) a RoutingUrl and the HttpContext to any method that needs this information. 

Finally we call the ExecuteRequest method, passing it this helper object and getting a ModuleRequestResult back (line 16).

Listing 5 – MvcModuleApplication, ExecuteRequest
   1:  public virtual ModuleRequestResult ExecuteRequest(ModuleRequestContext context) {
   2:      EnsureInitialized(context.HttpContext);
   3:   
   4:      // Create a rewritten HttpRequest (wrapped in an HttpContext) to provide to the 
   5:      //routing system
   6:      HttpContextBase rewrittenContext = new RewrittenHttpContext(context.HttpContext, 
   7:                                                  context.ModuleRoutingUrl);
   8:   
   9:      // Route the request
  10:      RouteData routeData = GetRouteData(rewrittenContext);
  11:   
  12:      // Setup request context
  13:      string controllerName = routeData.GetRequiredString("controller");
  14:      RequestContext requestContext = new RequestContext(context.HttpContext, routeData);
  15:   
  16:      // Construct the controller using the ControllerFactory
  17:      IControllerFactory factory = ControllerBuilder.GetControllerFactory();
  18:      IController controller = factory.CreateController(requestContext, controllerName);
  19:      try {
  20:          // DotNetNuke Mvc Modules must implement IModuleController (not just IController)
  21:          // Because we need to retrieve the ActionResult without executing it, IController
  22:          // won't cut it so attempt to adapt the Controller if it does not directly 
  23:          // implement IModuleController
  24:          IModuleController moduleController = controller as IModuleController;
  25:          if (moduleController == null)
  26:          {
  27:              moduleController = AdaptController(controller);
  28:          }
  29:          if (moduleController == null) {
  30:              throw new InvalidOperationException("Could not construct ModuleController");
  31:          }
  32:   
  33:          // Execute the controller and capture the result
  34:          moduleController.Execute(requestContext);
  35:          ActionResult result = moduleController.ResultOfLastExecute;
  36:   
  37:          // Return the final result
  38:          return new ModuleRequestResult {
  39:              ActionResult = result,
  40:              ControllerContext = moduleController.ControllerContext,
  41:              ModuleContext = context.ModuleContext
  42:          };
  43:      }
  44:      finally {
  45:          factory.ReleaseController(controller);
  46:      }
  47:  }

The ExecuteRequest method (Listing 5) is very similar to the ProcessRequest method of MvcHandler, and so it should be as we need to do the same things, construct a Controller class and execute the Controller so it can Invoke the appropriate action.  So lines 17 and 18 of Listing 5 are almost identical to lines 8 and 9 of Listing 3.

However there are a few differences.

  • At the beginning of the method we do some setup work that is handled by other classes in the ASP.NET MVC Pipeline (lines 2-10).
  • DotNetNuke MVC Modules must implement IModuleController (not just IController), so we attempt to cast the returner controller to IModuleController, and if that fails we attempt to adapt it (line 27)
  • After we call the Execute method of Controller we then use the IModuleController’s "ResultOfLastExecute property to trap the ActionResult, which is returnedd by the Controller’s “action” method (lines 34-35).
  • We generate a ModuleRequestResult to return to the caller code in OnInit (lines 38-42)

The important difference here is that unlike the main MVC Framework we need to trap the ActionResult.  Two helper classes allow us to achieve this.

MvcControllerAdapter and ResultCapturingActionInvoker classes

Listing 6 – The MvcControllerAdapter Class
   1:  public class MvcControllerAdapter : IModuleController
   2:  {
   3:      #region Private Members
   4:   
   5:      private Controller _adaptedController;
   6:      private ResultCapturingActionInvoker _actionInvoker;
   7:   
   8:      #endregion
   9:   
  10:      #region Constructors
  11:   
  12:      public MvcControllerAdapter(Controller controller)
  13:      {
  14:          _adaptedController = controller;
  15:          _actionInvoker = new ResultCapturingActionInvoker();
  16:          _adaptedController.ActionInvoker = _actionInvoker;
  17:      }
  18:   
  19:      #endregion
  20:   
  21:      #region IModuleController Members
  22:   
  23:      public ActionResult ResultOfLastExecute
  24:      {
  25:          get { return _actionInvoker.ResultOfLastInvoke; }
  26:      }
  27:   
  28:      public ControllerContext ControllerContext
  29:      {
  30:          get { return _adaptedController.ControllerContext; }
  31:      }
  32:   
  33:      #endregion
  34:   
  35:      #region Public Methods
  36:   
  37:      public void Execute(RequestContext requestContext)
  38:      {
  39:          if (_adaptedController.ActionInvoker != _actionInvoker)
  40:          {
  41:              throw new InvalidOperationException("Could not construct Controller");
  42:          }
  43:          ((IController)_adaptedController).Execute(requestContext);
  44:      }
  45:   
  46:      #endregion
  47:   
  48:  }

The MvcControllerAdapter class (Listing 6) allows us to adapt standard MVC Framework Controller’s so that they implement the additional IModuleController interface.  The main job of the MvcControllerAdapter is to replace the Controller’s ActionInvoker with a ResultCapturingActionInvoker (line 15).  The ResultCapturingActionInvoker (Listing 7) captures the result of the Action and exposes it as the ResultofLastInvoke property.

Listing 7 – The ResultCapturingActionInvoker Class
   1:  public class ResultCapturingActionInvoker : ControllerActionInvoker
   2:  {
   3:      #region Public Properties
   4:   
   5:      public ActionResult ResultOfLastInvoke { get; set; }
   6:   
   7:      #endregion
   8:   
   9:      #region Protected Methods
  10:   
  11:      protected override void InvokeActionResult(ControllerContext controllerContext, 
  12:                                                  ActionResult actionResult)
  13:      {
  14:          // Do not invoke the action.  Instead, store it for later retrieval
  15:          ResultOfLastInvoke = actionResult;
  16:      }
  17:   
  18:      #endregion
  19:  }

So why do we need to capture the ActionResult? 

In the standard MVC Pipeline the next phase after invoking the action is to render the View.  DotNetNuke modules need to render their View inside the Container provided.  The ExecuteRequest method of the MvcModuleApplication class returns the ModuleRequestResult (which contains the captured ActionResult) and saves it in a private variable.

Now, we need to remember that the MvcModuleApplication class inherits from the base Control class.  DotNetNuke will have instantiated this class and added it to the Controls collection of the Container.  We use this fact in the RenderControl method to render our View, by executing the ActionResult (which as long as a ViewResult was returned will render the View inside the Container). (see Listing 8)

Listing 8 – MvcModuleApplication, RenderControl
   1:  public override void RenderControl(System.Web.UI.HtmlTextWriter writer)
   2:  {
   3:      if (_moduleResult != null)
   4:      {
   5:          //Execute the ActionResult
   6:          _moduleResult.ActionResult.ExecuteResult(_moduleResult.ControllerContext);
   7:      }
   8:  }

And that’s how its done.

Conclusions

So in conclusion the process used to enable MVC modules to be created is as follows.

  1. During the Init phase of the Page Lifecycle, mimic the MvcHandler to instantiate an MVC Controller class
  2. Adapt the Controller to modify its ActionInvoker so we can capture the ActionResult from the Controller’s action method.
  3. During the render phase of the Page LifeCycle, execute the returned ActionResult, to render the View.

Posted in: DotNetNuke  Tags: , , ,

 Search Blog

 Adsense

 Calendar

«  March 2010  »
MoTuWeThFrSaSu
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234
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