I have called it “Classic Webforms” as it primarily uses features of DNN that were available prior to version 5, when we began to add additional ways to develop modules. However, as with most of my example modules this module is developed using a number of modern “Best Practices” – these Best Practices are highlighted in italic.
Nuget all the Things
Nuget was not available in the early days of DNN. If it was we would probably have never developed our own “Package Manager”. However, Nuget is one of the best tools available today in the .NET Open Source developer’s toolbag. I have therefore used the DotNetNuke.Core package available on nuget.org
Figure 1: Nuget Package Manager in Visual Studio 2015
Nuget allows me to choose the version of the package to install into my project. In this case I have 220.127.116.110 installed, and a new version – 18.104.22.168 is available. Nuget’s information is stored in the packages.config file stored in the project, and when a project is built in Visual Studio 2015, it will restore any files that it needs – so I don’t have to check the actual Nuget packages into Source Control.
Best Practice: Use Nuget packages whenever you need to take a dependency on a 3rd party library (including DotNetNuke class libraries).
Use a Module Database Prefix
DNN is quite a big project and there can be a lot of database objects. I therefore try and use a prefix for all of my database objects, which means they are all grouped together, which makes them easier to find. It also means that someone else is less likely to use the same object name. For example I have used the prefix ToDoDNN_
Best Practice: Use a database prefix for all your module’s database objects.
Use the Core DAL + Data Access Methods
If you intend to use stored procedures in your data layer, then make use of the DAL+ methods – ExecuteReader, ExecuteNonQuery etc. In the early days of DNN every module had to build its own data layer using an abstract DataProvider and concrete SqlDataProvider classes – with the DAL+ this is no longer needed and there is no benefit from building your own data layer.
Some developers add their own DataService class to their modules, as a wrapper to the core DAL+ methods – in fact I used to do this myself, and you can see examples of this in core DNN module projects – but my current opinion is that it doesn’t add any value – its just unnecessary ceremony.
Best Practice: Use the Core DAL+ Data Access Methods.
Create a Custom Settings Object
I know this is something that Peter Donker likes to do and I have come to agree with him. The standard way of exposing module settings is through the ModuleSettings and TabModuleSettings properties of the ModuleInfo class. These are just simple Hashtables and while there are now some helpful methods provided in the core to fetch correctly typed settings from a Hashtable, it makes a lot of sense to create a simple custom settings object, and localize all the settings data access in one location (see Figure 2).
Figure 2: The ToDoSettings Class
A custom settings object also makes the rest of the code more readable (see Figure 3), and more readable code is more maintainable. The use of magic strings for setting keys is localized in one place.
Figure 3: Using the ToDo Settings Class
Best Practice: Use a custom Settings class in your module project for all settings access.
Best Practices for your User Controls
When it comes to the presentation layer, there are a number of Best Practices that I would recommend.
First of all I always like to use ModuleUserControlBase as the base class for my User Controls, rather than PortalModuleBase. Many years ago, when I added new ways to create modules in DNN, I extracted the minimum necessary methods and properties into an interface - IModuleControl, and moved most of the extra methods and properties that PortalModuleBase exposed into the ModuleInstanceContext object (exposed as the ModuleContext property). PortalModuleBase was then refactored so that it implmented the IModuleControl interface, and as far as DNN is concerned all that is required is that a module control implements the IModuleControl. ModuleUserControlBase is a much tighter implementation, as it only adds a couple of Localization methods.
|Figure 4: The IModuleControl interface|
Best Practice: Inherit your user controls from ModuleUserControlBase.
Sticking to the code behind part of the user control, I prefer to override the OnLoad, OnInit and OnPreRender methods of the UserControl base class rather than implementing Load, Init and PreRender event handlers. This is a matter of preference but I feel it makes the code simpler and easier to read.
Best Practice: Override base class Lifecycle Methods rather than implementing Page Lifecycle Event Handlers.
DNN Corp have announced that they intend to remove the Telerik controls from the core DNN distribution, therefore it makes sense for you to avoid using any of the DNN wrapper controls found in the DotNetNuke.Web library.
Best Practice: Avoid the use of the Telerik wrapper controls found in the DotNetNuke.Web class library.
A couple of less obvious Best Practices involves the use of custom “ids” and “classes”. If your module is to play nicely with any DNN site you shouldn’t really be using any custom css classes, so make sure that you only use the css classes defined in default.css.
Best Practice: Only use css classes defined in default.css.
Now I recognize that sometimes the default styling in default.css is not necessarily what you want, so add an id to the outermost element in your user control (e.g. I have used toDoDNN_ViewToDos as the id for the outer div in the ViewToDos control. Then, when you create your own style in module.css, ensure that your selector includes the id, so that your module.css changes do not affect other modules or the core framework itself.
Best Practice: Use a unique id for the outermost element in your user control and use that id in any custom css selectors you define.
If you are building modules using the Classic WebForms approach, I hope you found this example project and blog helpful. However, I would suggest that in the modern world of Unit Testing and client-side development that if you are thinking of building a new module, you might want to choose a different approach. In future blogs, I will show how to update your Classic WebForms module to add Unit Testing and then I will show how you can break away completely from the use of WebForms and use a more modern development style.