Learn More





DNN Community Blog

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.

WebMatrix and DNN – 5 – Hosting the Razor Engine

In my previous post in this series on the new WebMatrix suite of technologies from Microsoft, I described how we are supporting the use of Razor scripts in DotNetNuke, by providing a “Razor Host” module.  In this article I will dive deeper into how we host the Razor Parser. 

Razor can actually be thought of as a templating engine and if you are planning on building a module that requires templates you might want to consider Razor.  Hopefully, this article will show how you might do that.

The WebPage Class

The WebPage Class (actually WebPageBase) is the basis of the new WebPages Framework, in the same way that the “Page” class is the basis of the WebForms Framework.

Essentially the approach we use in our “Razor Host” module is as follows.

  1. Identify the Razor script file (e.g. Twitter.cshtml)
  2. Call BuildManager.GetType(scriptFile) to create an instance of WebPage.  Note that the System.Web.WebPages assembly contains a special class – PreApplicationStartCode – that ensures that the extensions cshtml and vbhtml are registered with the BuildManager
  3. Call the ExecutePageHierarchy method of the WebPage class and capture the rendered content.
  4. Create a LiteralControl with the rendered content and add it to the control tree.

In order to achieve this there are two new classes in a new assembly (DotNetNuke.Web.Razor.dll) which is distributed as part of the “Razor Host” Module.

  1. RazorModuleBase – this class implements IModuleControl by sub-classing ModuleUserControlBase
  2. DotNetNukeWebPage – this class subclasses WebPageBase and provides some DNN specific enhancements.

The RazorModuleBase Class

The RazorModuleBase class is where everything comes together.  All DNN Modules need to implement the IModuleControl Interface – this how the module injection logic knows what to inject.  RazorModuleBase is a new base class that implements IModuleControl and is used as the base class for the Razor Host Module.

In the OnPreRender method of the class we first check if our script - RazorScriptFile – exists.  If it does we call the CreateWebPageInstance method to get an instance of  the DotNetNukeWebPage (lines 7-16)

Figure 1 – The OnPreRender method of the RazorModuleBase class

   1:  Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
   2:      MyBase.OnPreRender(e)
   4:      Try
   5:          If File.Exists(Server.MapPath(RazorScriptFile)) Then
   7:              Dim instance As Object = CreateWebPageInstance()
   8:              If instance Is Nothing Then
   9:                  Throw New InvalidOperationException( _
  10:                      String.Format( _
  11:                          CultureInfo.CurrentCulture, _
  12:                          "The webpage found at '{0}' was not created.", _
  13:                          RazorScriptFile))
  14:              End If
  16:              Dim webPage As DotNetNukeWebPage = TryCast(instance, DotNetNukeWebPage)
  18:              If webPage Is Nothing Then
  19:                  Throw New InvalidOperationException( _
  20:                      String.Format( _
  21:                          CultureInfo.CurrentCulture,
  22:                          "The webpage at '{0}' must derive from DotNetNukeWebPage.",
  23:                          RazorScriptFile))
  24:              End If
  26:              webPage.SetContext(Me.ModuleContext)
  27:              InitHelpers(webPage)
  29:              Dim writer As New StringWriter
  30:              webPage.ExecutePageHierarchy(New WebPageContext(HttpContext), writer, webPage)
  32:              Controls.Add(New LiteralControl(Server.HtmlDecode(writer.ToString())))
  33:          End If
  34:      Catch ex As Exception
  35:          ProcessModuleLoadException(Me, ex)
  36:      End Try
  39:  End Sub

We then set the WebPage’s module context by calling its SetContext method (line 26).  This allows us to access the ModuleContext in Razor script.  Finally we call the WebPage’s ExecutePageHierarchy method to get the rendered content, which we add as a LiteralControl to the Controls collection of the module.

In the above description I glossed over two important points: the RazorScriptFile property and the CreateWebPageInstance property.

The RazorScriptFile property (Figure 2) returns the virtual path of a “default” script file.  In the Razor Host module, we override this property and return the currently selected script, but the default behaviour in the base class is to return “MyScript.cshtml” if the current control is “MyScript.ascx”.

Figure 2 - The RazorScriptFile property

   1:  Protected Overridable ReadOnly Property RazorScriptFile As String
   2:      Get
   3:          Return Me.AppRelativeVirtualPath.Replace("ascx", "cshtml")
   4:      End Get
   5:  End Property

The CreateWebPageInstance method (Figure 3) uses the RazorScritpFile property to create an instance of a DotNetNukeWebPage (Figure 3), calling BuildManager.GetCompiledType (line 2), which returns a Type and then calling Activator.CreateInstance (line6) to return an instance of the DotNetNukeWebPage.

Figure 3 – CreateWebPageInstance

   1:  Private Function CreateWebPageInstance() As Object
   2:      Dim type As Type = BuildManager.GetCompiledType(RazorScriptFile)
   3:      Dim instance As Object = Nothing
   5:      If type IsNot Nothing Then
   6:          instance = Activator.CreateInstance(type)
   7:      End If
   9:      Return instance
  10:  End Function

Earlier in this article I mentioned that BuildManager knew to “build” a WebPage instance from a cshtml or vbhtml because of some code that exists in the WebPages Framework that registers the extension.  So why do we get an instance of DotNetNukeWebPage rather than an instance of WebPage? 

The answer lies  in the included web.config file that sits in the same folder as the Razor Host Module.  By default the Razor Engine will return an instance of WebPage, but this can be modified in the new system.web.webPages.razor section of web.config, where we have indicated that an instance of DotNetNukeWebPage should be returned.

Figure 4 – Razor Host Module’s web.config file

   1:  <system.web.webPages.razor>
   2:    <pages pageBaseType="DotNetNuke.Web.Razor.DotNetNukeWebPage">
   3:      <namespaces>
   4:        <add namespace="Microsoft.Web.Helpers" />
   5:        <add namespace="WebMatrix.Data" />
   6:      namespaces>
   7:    pages>
   8:  system.web.webPages.razor>

At the moment we are just including this web.config file as part of the module, but in the final release we will probably update the web.config file in the website root to include the information.

So that was for all the Architect types who want to know how it was done.  In the next blog I will show how you can “package” up a script you have created and distribute it to other DNN users.

This article is cross-posted from my personal blog.


Comment Form

Only registered users may post comments.


2sic Daniel Mettler (125)
Aderson Oliveira (15)
Alec Whittington (11)
Alex Shirley (10)
Andrew Nurse (30)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (22)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (203)
Chris Paterra (55)
Clinton Patterson (28)
Cuong Dang (21)
Daniel Bartholomew (2)
Dave Buckner (2)
David Poindexter (3)
David Rodriguez (2)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (74)
Geoff Barlow (6)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (270)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matthias Schlomann (15)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Mike Horton (19)
Mitchel Sellers (28)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Peter Donker (52)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott S (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (2)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (37)
Will Strohl (163)
William Severance (5)
Try Evoq
For Free
Start Free Trial
a Demo
See Evoq Live
Need More Information?