How do you build a Dnn Module?
I occasionally read forum threads and blog comments which discuss the myriad of different ways of building a Dnn module. Usually this is directed towards building modules for storing and displaying content. Sometimes those are negative – the author is overwhelmed by choice and doesn’t know where to start.
Is it really a problem, though? Is the problem one of choice, or one of clearly defined paths?
Flipping this upside down, I see that as a strength of the Dnn Platform as it can cater for a wide audience. As Dnn powers everything from hobby club sites to multinational corporate websites and everything inbetween, a one-size-fits-all approach would be a negative. It’s a strength you can choose your own method to build what you want.
Modules built for resale need to be well architected and stick to standards for maximum compatibility and interoperability. Modules built for a specific corporate solution need to be well made and designed for maintenance and testability, but can at least build to a targeted version. Modules built for smaller projects need to be biased towards speed to market and easy of update, especially when unpaid work hours are at a premium.
With that background to module styles complete, only a madman would introduce yet another way to Dnn modules. Here goes, anyway - what I’m going to demonstrate is a way of building modules which will attempt to lasso all of these trends and get them to pull our coding stagecoach.
Specifically, the module must allow:
1. Single Page Applications (SPA) – performing all the interaction on a single page without subsequent reloads.
2. Building and running sites in the cloud, and using tools like Visual Studio Online to make changes, rather than having a local development environment and heavy toolset.
3. Cross-platform development where the person making the changes may not even be able to run Visual Studio.
4. Moving away from code-behind models like Webforms
5. Ensuring SEO is front-and-centre during the development phase
Quite a diverse list to pull together and use to create yet another module development template! Let’s get started by looking at how we can work with these trends by declaring the tools and architecture.
Solving the SEO and SPA divergent goals.
SEO requires control over the URL, Page meta description and Title, and fast-loading static content that doesn’t require script to execute. Modern search engines do execute script – but optimization is making sure the content meets best practice.
SPA means using AJAX techniques to interact with the page instead of reloading entire pages with postbacks and links to follow. This can be in conflict with SEO because a SPA might require script to load the initial content for the page.
Solving these divergent goals requires building code that provides an initial view of the content, and then uses API calls via AJAX to load subsequent content on interaction. The code architecture must allow for a unified way of treating the content two types of view in the same way. The DNN Page controls provide the ability to set the URL, Title and Description, so this module only concerns itself with presenting static content when the page is loaded.
Building applications on varied devices, including Cloud based development
One of the neat things you get in Azure App Services (the renamed Azure Websites product) is Visual Studio Online – which was code-named Monaco. This is a save-and-run text editor and basic development environment hosted in the browser. Now, this isn’t even 1/100th of what a full local development IDE can do, but it is fast, convenient and cross platform. Cross platform also means development with other tools like Sublime Text or any other code/text editor that takes your fancy. Moving away from Visual Studio means moving away from MSbuild and compiled solutions and toward edit-and-run changes. Which brings us to the neatly to the next topic, Webforms and compiled development.
Moving away from Webforms
The movement in ASP.NET development is definitely away from Webforms – this is not recent news. While DNN is heavily based on the Webforms model, it’s really only the administrative side of things that is dependent on that technology. Indeed, most of the recent cool functionality in Evoq Content and Evoq Engage is not Webforms based at all. Indeed, the next major DNN release has MVC functionality built in.
Webforms modules are highly functional and performant if built well, but the code-behind-and-compile model doesn’t fit well with the other goals laid out. So we need to build with something else, so the Razor view functionality is the perfect choice.
Gathering our design together, what we end up with is the following architecture:
• A Dnn module built using Razor views and WebAPI, integrated in such a way that there are not two different code libraries for serving each display type.
• Built in an edit-and-run way, which disposes with the pre-requisite of having Visual Studio (full version) and allows for changes with Visual Studio Online or other text-based code editors.
Here’s how I built that, and how you can re-use the code as a template for your own projects.
NOTE : If there are any code architects reading who just drew sharply through their teeth, it’s true, there are a mixture of patterns here. I’m providing the barest outline of a module, how well you follow your ViewModel for MVVM and Razor is entirely up to you. Again it depends on where in the continuum you expect your code to land. I suspect anyone building a distributable module will use a compiled project, and anyone doing one-off modules in an environment with strict development controls may not prefer these methods. For everyone else it might provide a simple way forward. Don’t let architecture perfection be the enemy of finished and functional code.
A new Dnn Module Pattern
All the above points lead into a new Dnn module pattern I have made. It’s a Dnn module, but uses different architecture to what you might be used to seeing.
Razor Views
A Razor view is a plan Html file with embedded markup allowing interaction with objects and variables from the underlying code. Razor views were introduced in 2010, so are hardly new technology. The advantage of a Razor view is the simplicity in the markup, which places more emphasis on getting things done. A Razor view should be thought of as a simple templating engine. The resulting Html is much closer in appearance to the template Html, avoiding Html bloat which causes slower pages and broken designs when translated to runtime.
Building Razor with Dnn has been supported for a long time (since 2010, actually) but takeup in my experience has been limited. Razor brings with it compile-free operation – you just change the view and refresh the page and the change is immediate.
In part that may be because of the paucity of examples of how actually to build a Dnn module with Razor – something that this post seeks to address.
WebAPI and SPA
WebAPI was introduced with DNN 7 and allows an easy way of building API endpoints which can interact with your webpages. Using WebAPI and a client-side binding framework like Knockout or Angular allows fast creation of Single Page Applications. There are a lot of existing blog posts and other reference material to WebAPI so I won’t cover it in more detail. See the WebAPI Tips Wiki page for a good starting point.
Using App_Code Folders
The App_Code folder in ASP.NET is a special folder which allows native C# or VB code to be placed directly into the site without compiling. Dropping the code in there forces the application to be rebuilt and the compiled classes are then accessible within the application. This can be used to build any piece of functionality including the Controllers for WebAPI methods and the back end to support Razor views. You should be alert by now to where this is going.
Hello World Example Module
To demonstrate these techniques in a functional module, I have created a Hello World Razor/WebAPI module. You can download and install this in any DNN 7.3 or later installation.
This mixes delivery of a common model (the HelloWorldModel) to the page in two distinct ways – via the Razor View and via client-side script calling the WebAPI.
While you can’t see it in the above image, the first line is delivered by Razor and is ‘static’ in that it is there when the page is built.
The second line is delivered via the WebAPI and added by client side code.
Conceptually it can be shown like this:
There is a base class called RzrViewModel – the Model or ViewModel (your preference) should inherit from this. This provides the ability for all Models/ViewModels to share common properties. In this case, it’s sharing the property for the location of the Module WebAPI endpoint, but could be expanded for other Module specific properties.
The HelloWorldModel also implements an interface called IWebAPIResponse which defines a common set of values useful for working in client side code.
By inheriting from RzrViewModel and implementing IWebAPIResponse, the model is ready to work through both methods of taking data from the server to the device browser.
Connecting the browser to the WebAPI code is done through a Controller class, associated with a RouteMapper class, as is common in all WebAPI code. We then just use some js code to call into the WebAPI, inspect the result and manipulate the DOM as needed. Note that this example doesn’t use any MVVM binding like Knockout or Angular – that’s a topic for a different post.
Connecting the browser to the Razor View is done via a class called RzrViewModelLoader, which contains a specific method for returning a HelloWorldModel. This is done using an extension method to the DnnHelper object. This uses the Dnn Razor code to associate with a specific view.
Unlike the WebAPI code, this is by configuration rather than by convention. The WebAPI code knows which method to call because of the naming standards of the Controller and the method, compared with the API call. The Razor view doesn’t have that (as it might in an MVC project) so we have to explicitly bind the two together.
Inside the Razor view (_view.cshtml), we declare the HelloWorldModel as the root property for the view, and then we can use Razor syntax to access the properties. This binds the objects with the template, and allows Razor code blocks to be used.
In the Razor View we also can access the property of the RzrViewModel base class to return the correct API. This might sound trivial, but making portable, flexible code relies on minor setup like this. Many an API is broken when it is copied from one site to another because the endpoint was hardcoded.
Hosting the Razor view is done with a single ‘view.ascx’. The view.ascx file is still the file used in the Dnn module manifest to load when the module is added to the page. This is a container to get the Razor view into Dnn. All modules have to have a user control of some type. The other use for the User Control is to leverage the Client Resource Management tools to include the links for javascript we need on the page. There is no code-behind for this user control (a file exists, but it is empty).
You could include more code-behind user controls to leverage the standard Dnn controls like module settings, or you can build the loading of a settings page into your SPA. There is no need to have a compiled module even with code-behind ascx files.
Online or Cross-Platform Code Editing
All the above code can be written on any text editor – no Visual Studio or MSBuild needed. Visual Studio makes the experience nicer, as you get Intellisense and have the ability to run a build on the website to see if you have compile errors – but it’s not a pre-requisite.
Here’s the same file loaded in Visual Studio Online, in an Azure App (Azure Website)
This is where the real benefit comes in – you can edit the files in a copy of your live site, then, when it’s all ready, swap it for the live site in an instant. This type of instant coding was unheard of the first time I started writing a Dnn module.
Get the code
You can download the code from here:
HelloWorld_Module_01.00.00_Install.zip
Remember, because the source code is compiled at run time, the Install package is also the source code.
The code uses the naming standard of ‘Corp_Name’ ‘Corp_Module’. This makes it easy to do a global find/replace on ‘Corp_Name’ with your name (or Organization name) and ‘Corp_Module’ with your choice of module name.
Moving beyond Hello World
This simplistic module was designed only to sketch out the architecture for a Razor/WebAPI module using some commonality. There are a range of topics not covered, including settings, database access, search integration, permissions and many other Dnn specific topics.
I will shortly post a second entry in this series which will take this concept further and build a database CRUD app using the same base. This will provide a more realistic look at building a functional Dnn SPA using these techniques.
In the meantime, please give the module a try, inspect the code and tell me what you think. I present the source to you, the Dnn Community, in the hope that you might critique it and make it better.
Comments
I’m interested in what people think of the concept. I’m happy to put the code on Github if people wanted to contribute their own ideas, and stay tuned for Part 2.
Update: Part 2 is now here - see Build a SPA module with Razor, WebAPI, Knockout and jQuery