DotNetNukeŽ Case Study: DNNTreeView for Large DNN Applications

When deploying large DotNetNuke applications, you may consider using a custom menu that allows granular access to individual module controls. This will allow you to still group and deploy collections of controls as module packages yet present each individual control as a separate page to the end user.

The Problem

I recently ran into a long time DotNetNuke developer who indicated that a recent large DNN site he deployed had to be converted to MVC because it could not take the user load. I have deployed large scale DNN applications successfully, so I asked him how many pages or "Tabs" he had in the site. He told me "lots". I determined that was the difference. When I deploy large DNN applications I usually have only 1-4 pages in the site. I usually put all the functionality in one or two big modules. I usually do not use any custom Roles. You are either logged in as a "Registered User" or you are not logged in. All other permissions I handle in my custom tables that are optimized for the required functionality.

The DotNetNuke Core Roles and menu management is powerful and useful, but it is very resource intensive. When a user logs into your site, the DotNetNuke Core code determines what their "Menu" should look like and it caches it, but if the server is overloaded it will flush the cache and the database will be hit instead. In addition, it may not provide the flexibility that you may desire without jumping through a lot of resource intensive loops.

The Requirement

I recently had the following requirement:

  1. All functionality and navigation is to be displayed in a Tree View that always stays on the left side of the screen
  2. There will be a large number of Roles that need access controlled for each and every page
  3. The ability to view and edit each item on each page needs to be controlled by Role security

The Issues

(1) One may be tempted to just use the Core Tree Menu display. You can place each user in a Role and set the access to each page and then place each module on the page. The problem with this is that if you have a lot of pages and a lot of Roles, a lot of calculations need to be made by the DNN Core menu code.

(2) The core DotNetNuke access control code controls access to individual modules. The default .ascx control(s) of each module display and the user must click on a link or a menu item to navigate to additional .ascx controls in the module. When a user does navigate to one of the additional .ascx controls, all the other modules on the page disappear including any custom menu module.

(3) While it is possible to determine what Role a user is in using the DotNetNuke API, it is not as efficient as it would be to query the database tables directly. However, if you access the database tables directly you risk having your code break when the site is upgraded to a new version.

A Solution

The DNNTreeView module is a proof of concept that addresses these issues. This is based on an article "Dynamic Load User Controls in DotNetNuke" by Fuji (mcsenow) Nguyen. The major alteration is the inclusion of the DynamicControlsPlaceholder control by Denis Baur to allow ViewState to be maintained for the dynamically created controls.

When you install the module and place it on a page, the portal Administrators will see a "Edit Menu Items" link.

The Administrator can add items to the menu by selecting an .ascx control and indicating a link name.

Clicking the "[Back]" link will display the menu.

Clicking the Silverlight Chat link will display the Silverlight Chat application and it will work as usual.

The Fast Login module will display when selected...

but it will throw an error when it executes code that attempts to use the ModueId.

If we debug the module we realize that where we would normally have a ModuleId we instead have "-1".

            If txtModuleID.Text = "" Then
                txtModuleID.Text = ModuleId.ToString
            End If

If we save the ModuleID to a hidden text field in the Page_Load...

It will be available on PostBack. This solves the problem and the module now works normally.

You can even implement recursive module loading, The DNN Tree Menu link (that we configured earlier to point to to module itself) will bring up an instance of itself and you can keep clicking on itself to bring up more instances of itself...

Exploring the Solution

So the concept will work but it has it's limitations that you have to code around. However, it does address the 3 requirements:

(1) We have complete control over the Roles (because we have created our own custom menu) so we can easily design the most efficient database tables and user interfaces to control access.

(2) The menu will stay on the left side of the screen. We can use code such as the Category Administration Module to create and administer a Tree View of nested menu items.

(3) Because we are using a custom Role management, we can implement methods that can efficiently determine access for each item on a .ascx control. We can do this because we can easily create a table that lists each item that is on a page and assign access to each each item by Role. Yes we could also do this using the DotNetNuke Core Roles but it is not easy to efficiently query a persons access if they belong to multiple roles when using the DotNetNuke Core API.

The Code

The code is actually simple and straightforward. In the example code, there is a GridView populated with the Links and the path to the .ascx control as the link "argument". When the link is clicked, the following code dynamically loads the .ascx control into the DynamicControlsPlaceholder:

        protected void gvMenu_RowCommand(object sender, System.Web.UI.WebControls.GridViewCommandEventArgs e)
        {
            DotNetNuke.Entities.Modules.PortalModuleBase objModule = 
                (DotNetNuke.Entities.Modules.PortalModuleBase)this.LoadControl(
                String.Format("~/DesktopModules/{0}", e.CommandArgument.ToString())
                );
            DynamicallyLoadControl(objModule);
        }

        private void DynamicallyLoadControl(DotNetNuke.Entities.Modules.PortalModuleBase objModule)
        {
            if ((objModule != null))
            {
                objModule.ID = "DynamicPage";
                objModule.ModuleConfiguration = this.ModuleConfiguration;
                DCP.Controls.Clear();
                DCP.Controls.Add(objModule);
            }
        } 

Basically the .ascx control that inherits from PortalModuleBase is cast as a PortalModuleBase class and dynamically added to the DynamicControlsPlaceholder panel.

This will work but you wont need it

Normal DotNetNuke Core menu functionality will work fine for almost all sites out there. If your site is moving slow try a better hosting provider with more bandwidth and more memory on the server. What were describing here is "bring the server to it's knees", "hundreds of users hitting the server at the same time" situation. If you're deploying sites like that, you may find some ideas here you may want to consider. 

Download the code: DnnTreeMenu_01.00.00_Install.zip (install and source)
Note: If using DNN4 install and run LinqPrep first.

[Back to: The ADefWebserver DotNetNuke HELP WebSite]


DotNetNukeŽ is a registered trademark of the DotNetNuke Corporation