锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 / ASP.NET开源 / DotNetNuke / DotNetNuke SiteMap
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

DotNetNuke SiteMap


Introduction简介

I migrated a part of a big site (> 500 pages) to DotNetNuke which is an Open Source .NET CMS. After migration, the loading of each page was very slow (10 sec!). After investigating, I found out that the SiteMap (a TreeViewcontrol) was the source of the time delay. So, I tested three other SiteMap controls, which I found on the Internet. All had nearly the same result, they were much too slow. My first thought was that the reading of 500 pages was time consuming. So I made a test program, and found out that the reading from the DB was finished in no time, the building of a tree data structure with pages as nodes (with parents) was finished in no time, even the filling of the .NET 2.0 web TreeView control was very quick. The bottleneck was the rendering of the MS web TreeViewcontrol. I assume that the other, slower, SiteMaps always filled the complete tree, which then took the TreeViewcontrol to render that long, even if most of the nodes were collapsed. And that was my idea, to speed that process up because you normally have only a small bit of your tree expanded, you simply don't need the data from other leaves, because no one is watching them. And if the user expands a node, then I get the children from the DB and fill just that one node. Another feature of DnnSiteMap is that it realizes on which page, which is called a Tab in DNN slang , it is, and expands itself to that node and selects it.我把一个大网站的部分页面(至少500个页面)升级到DotNetNuke平台下。在升级后,每个页面加载非常慢(10秒)。经过调查,我查出SiteMap(TreeViewcontrol)是主因。所以,我测试了3个其它的SiteMap控件。3个几位结果一样,它们都很慢。第一个想法是500个页面的读取比较耗时。所以我写了个测试程序,从DB读取会很快完成,树形数据构造很快,甚至填充.NET 2.0 web 树形控件也很快。瓶颈在于web 树形控件的渲染。我假定慢的SiteMaps总是完整填充树形,完整填充会让树形控件渲染时间长,即使大部分节点是折叠的。基于通常只需要加载部分节点,为了加快处理,简化过程,不需要其它叶子节点的数据,因为没人看这些节点。如果用户展开节点,那么我从DB里获取子节点信息,填充到展开节点下。DnnSiteMap的另外特点是依赖了DNN的Tab控件,Tab会展开自己到选中节点下,且Tab会被先用。

Important: DnnSitemap is for DotNetNuke 4 and APS.NET 2.0 only! For installation, you must follow the install instructions (see below).DnnSitemap在DotNetNuke 4 和 APS.NET 2.0平台提供。请遵守如下安装说明。

Background

DotNetNuke is an open source .NET Content Management System. It is derived from the IBuySpyPortal, which is a best practice from Microsoft to show the capabilities of ASP.NET. Currently, DotNetNuke (DNN) is in version 4 which is based on the new ASP.NET 2.0 and is programmed in VB.NET. Because of its big community support, MS is supporting the DNN project. DNN is programmed by a core team, lead by Shaun Walker.

Features

  • Quick SiteMap because the tree is only filled on demand, from the DB.
  • Root Tab (Page) can be defined via settings.
  • Show Lines can be set via settings.
  • Node text word wrap can be set via settings.
  • Predefined icon set (many included) can be set via settings.
  • Panel control (collapse all/expand all/ current) can be shown or hidden via settings.
  • Flag if only tabs are visible, from which the user has permissions to view them.
  • Node indent in pixels can be set via settings.

Using the code

I did a bit of over-commenting inline of the code, so that anyone can understand what each step is doing. Basically, the code is straightforward. All the data and the business logic is in the App_Code folder, and the UI code is in the ViewDnnSiteMap.ascx.cs file.

In the data layer, you will find the following functions:

/// <summary>  
/// Gets all tabs that have no ParentId   
/// and are not deleted and visible  
/// </summary>  
/// <returns>IDataReader: TabId (int), TabName (string),   
/// Children (int)</returns>  public abstract IDataReader GetRootNodesFromDb();    
/// <summary>  
/// Gets all tabs that are children of the specified tab  
/// </summary>  
/// <param name="parentTabId">TabId of the parent tab</param>  
/// <returns>IDataReader: TabId (int), TabName (string),   
/// Children (int)</returns>  public abstract IDataReader GetChildNodesFromDb(int parentTabId);
/// <summary>  
/// Gets parent tab for specified tab
/// </summary>  
/// <param name="childTabId">TabId of the child tab</param>  
/// <returns>IDataReader: ParentTabId (int), ParentName (string);  
/// (should be max one row)</returns>  public abstract IDataReader GetParentFromDb(int childTabId);    
/// <summary>  
/// Gets the Tab, that hosts the given module  
/// </summary>  
/// <param name="tabModuleId">TabModuleId of the module</param>  
/// <returns>IDataReader: ParentTabId (int), ParentName (string);  
/// (should be max one row)</returns>  public abstract IDataReader GetTabViaTabModuleIdFromDb(
int tabModuleId);    
/// <summary>  
/// Gets node with specified TabId from Db  
/// </summary>  
/// <param name="nodeTabId">TabId for node</param>
/// <returns>IDataReader: TabId (int), TabName (string),   
/// Children (int)</returns>  public abstract IDataReader GetNodeFromDb(int nodeTabId);

You can find the implementation of these functions in the SqlDataProvider.cs file. They are basically simple SQLSELECT statements. In future releases, these will be in a stored procedure, to gain some extra performance.

The business layer can be found in the controller class in DnnSiteMapController.cs. The functions are:

/// <summary>  
/// Retrieves all visible root nodes from Db  
/// </summary>  
/// <returns>List of root nodes as ExtendedTreeNode  
/// </returns>  
public List<ExtendedTreeNode> GetRootNodesFromDb()    
/// <summary>  
/// Retrieves Child Nodes from Db for given Node  
/// </summary>  
/// <param name="parentNode">ParentNode,   
/// for which the children should be retrieved</param>  
/// <returns>List of children as ExtendedTreeNode  
/// </returns>  public List<ExtendedTreeNode> GetChildNodesFromDb(
TreeNode parentNode)    
/// <summary>  
/// Gets the navigation path for a given Tab to the root  
/// </summary>  
/// <param name="childTab">Tab for   
/// which the path should be retrieved</param>  
/// <returns>List of Tabs, begining with the root   
/// and ending with the Child</returns>  public List<Structs.Tab> GetNavigationPathFromDb(
Structs.Tab childTab)    
/// <summary>  
/// Gets the Tab, that hosts the given module  
/// </summary>  
/// <param name="tabModuleId">TabModuleId of the module  
/// </param>  
/// <returns>Dnn TabId</returns>  public Structs.Tab GetTabViaTabModuleIdFromDb(int tabModuleId)    
/// <summary>  
/// Gets node with specified TabId from Db  
/// </summary>  
/// <param name="nodeTabId">TabId for node</param>
/// <returns>Specified node; null if node is not found  
/// </returns>  public ExtendedTreeNode GetNodeFromDb(int nodeTabId)

The UI code is in the ViewDnnSiteMap.ascx.cs file. In the Page_Load event, the settings are applied and the root nodes are retrieved from the DB. Then, the tree expands to the current tab (the page which is hosting the control):

protected void Page_Load(System.Object sender,
System.EventArgs e)  {
try
{
if (!IsPostBack)
{
// controller class              
DnnSiteMapController objDnnSiteMaps =
new DnnSiteMapController();
// config settings              
ConfigurationSettings settings =
new ConfigurationSettings(this.Settings);
// set show lines
this.TreeView1.ShowLines = settings.ShowLines;
// set image set
this.TreeView1.ImageSet = settings.ImageSet;
// set node wrap              
this.TreeView1.NodeWrap = settings.NodeWrap;
// set show controls              
this.pnlControls.Visible = settings.ShowControls;                
// set node indent              
this.TreeView1.NodeIndent = settings.NodeIndent;
// fill root nodes or specified rootNode              
this.FillRootNodes(settings.RootNode);
// get current TabId from DNN and expand to it              
this.ExpandToTab(this.TabId);
}
}
catch (Exception exc) 
//Module failed to load      
{
Exceptions.ProcessModuleLoadException(this, exc);
}
}

In the TreeNodeExpanded event, I check if the node already has its children, if not, I retrieve them from the DB:

protected void TreeView1_TreeNodeExpanded(object sender,
TreeNodeEventArgs e)  {      
// if node has DummyNode, else data was       
// already retrieved from Db      
if (NodeHelper.HasDummyNode(e.Node))
{          
// controller class          
DnnSiteMapController objDnnSiteMaps =
new DnnSiteMapController();
// clear child nodes          
e.Node.ChildNodes.Clear();            
// for all child nodes          
foreach (ExtendedTreeNode childNode in
objDnnSiteMaps.GetChildNodesFromDb(e.Node))          
{              
// if root has children, add dummy node              
if (childNode.HasChildren)
{                  
NodeHelper.AddDummyNode(childNode);
}                
// add children to expanded node
e.Node.ChildNodes.Add(childNode);
}
}
// select current node      
this.SelectCurrentNode();  
}

The private ExpandToTab(int tabId) method is used in the Page_Load event. This method is used to expand the tree to a specified node and select it. This is very handy in the Page_Load event, because you can set the tab to the current page:

private void ExpandToTab(int tabId)  {      
// controller class      
DnnSiteMapController objDnnSiteMaps =
new DnnSiteMapController();        
// collapses all nodes; IMPORTANT to use this function      
// instead directly TreeView.CollapseAll(), because      
// it can loose all nodes      
this.CollapseAll();        
// find node in tree view (no roundtrip to Db)      
TreeNode node =           
NodeHelper.GetNode(this.TreeView1.Nodes, tabId);        
// check if node is already in tree view      
if (node != null)      
{          
TreeNode currentNode = node;            
// expand to node          
while (currentNode != null)          
{              
currentNode.Expand();              
currentNode = currentNode.Parent;
}
}      
else 
// get parent path from Db      
{
List<Structs.Tab> parentTabs =
objDnnSiteMaps.GetNavigationPathFromDb(                   
new Structs.Tab(tabId, String.Empty));
TreeNode currentNode = null;
// expand all nodes along path
foreach (Structs.Tab nodeTab in parentTabs)          
{              
currentNode =                 
NodeHelper.GetNode(this.TreeView1.Nodes,
nodeTab.TabId);                
if (currentNode != null)              
{                  
currentNode.Expand();              
}          
}      
}        
// select current node      
this.SelectCurrentNode();  
}

Points of interest

DNN installation instructions

Step 1: Install the module via DNN

Login in as host, and select in the Host menu "Module Definitions". At the bottom of the page, press "Upload new module". Then, select the .zip file, add it, and then upload it.

Step 2: Alter web.config file (without this, DNN won't work anymore)

The module is written in C#, you must include the following lines in your web.config file, in the <system.web> <compilation> section (it should already be there, but commented out):

<codeSubDirectories>     
<add directoryName="DnnSiteMap"/>  
</codeSubDirectories>
Value property of the TreeNode class

Because every tab (page) in DNN has a TabId, I save that information with every TreeNode. For that, I use thevalue field. It is of type string, so, I convert it to int whenever needed.

ExtendedNode class

The ExtendedNode class is derived directly from the System.Web.UI.WebControls.TreeNode class. It adds the boolean field HasChildren to the class. This is used when I retrieve the nodes from the DB; then I set that flag to true, if they have child nodes. With this little trick, I can spare an extra roundtrip to the DB.

DummyNodes

The TreeView control shows a plus sign to expand a node, if it has at least one child node. Because I don't want to retrieve nodes as long as the parent is expanded, I fill them with a DummyNode. To differentiate from a normal node, I set their value field to -1.

ConfigSettings

I encapsulated the settings in the class ConfigurationSettings. It reads the settings out of a Hashtable (e.g.,ModuleSettingsBase.TabModuleSettings or PortalModuleBase.Settings), makes them type-safe, and initializes them with default values.

Roadmap

New features can be: using stored procedures; defining custom CSS classes .... So, there is still lots to do.

友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州大学北校区院(文化路97号院)内