DotNetNuke 站点地图






4.54/5 (15投票s)
一个适用于 DotNetNuke 4 的网站地图模块,它按需检索数据,对于拥有大量页面的网站仍然非常快速。
引言
我将一个大型网站(超过 500 页)的一部分迁移到了 DotNetNuke,这是一个开源的 .NET CMS。迁移后,每个页面的加载速度非常慢(10 秒!)。经过调查,我发现 SiteMap
(一个 TreeView
控件)是造成延迟的原因。因此,我测试了我在网上找到的其他三个 SiteMap
控件。它们的结果都差不多,太慢了。我最初的想法是读取 500 页很耗时。所以我编写了一个测试程序,发现从数据库读取数据很快就完成了,用页面作为节点(带父节点)构建树数据结构也很快就完成了,甚至填充 .NET 2.0 web TreeView
控件也很快。瓶颈在于 MS web TreeView
控件的渲染。我猜想其他较慢的 SiteMap
总是填充整个树,这导致 TreeView
控件渲染时间很长,即使大多数节点都是折叠的。这就是我的想法,加快这个过程,因为你通常只展开树的一小部分,你根本不需要其他叶子节点的数据,因为没有人查看它们。如果用户展开一个节点,那么我就从数据库获取子节点并只填充该节点。DnnSiteMap
的另一个特点是,它能识别它所在的页面(在 DNN 行话中称为 Tab ;-)),并展开到该节点并选择它。
重要提示:DnnSitemap
仅适用于 DotNetNuke 4 和 APS.NET 2.0!对于安装,您必须遵循安装说明(见下文)。
背景
DotNetNuke 是一个开源的 .NET 内容管理系统。它源自 IBuySpyPortal,这是 Microsoft 展示 ASP.NET 功能的最佳实践。目前,DotNetNuke (DNN) 的版本是 4,它基于新的 ASP.NET 2.0,并使用 VB.NET 编程。由于其庞大的社区支持,MS 正在支持 DNN 项目。DNN 由 Shaun Walker 领导的核心团队开发。
特点
- 快速的
SiteMap
,因为树只按需从数据库填充。 - 可以通过设置定义根 Tab(页面)。
- 可以通过设置显示线条。
- 可以通过设置节点文本自动换行。
- 可以通过设置预定义图标集(包含许多)。
Panel
控件(全部折叠/全部展开/当前)可以通过设置显示或隐藏。- 如果只显示用户有权限查看的选项卡,则标记。
- 可以通过设置节点缩进像素。
使用代码
我在代码中添加了一些过多的内联注释,以便任何人都能理解每个步骤的作用。基本上,代码是直接的。所有数据和业务逻辑都在 App_Code 文件夹中,UI 代码在 ViewDnnSiteMap.ascx.cs 文件中。
在数据层中,您将找到以下函数
/// <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);
您可以在 SqlDataProvider.cs 文件中找到这些函数的实现。它们基本上是简单的 SQL SELECT
语句。在未来的版本中,它们将放在存储过程中,以获得额外的性能。
业务层可以在 DnnSiteMapController.cs 中的控制器类中找到。函数是
/// <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)
UI 代码在 ViewDnnSiteMap.ascx.cs 文件中。在 Page_Load
事件中,应用设置并从数据库检索根节点。然后,树展开到当前选项卡(托管控件的页面)
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);
}
}
在 TreeNodeExpanded
事件中,我检查节点是否已经有其子节点,如果没有,我从数据库中检索它们
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();
}
private ExpandToTab(int tabId)
方法用于 Page_Load
事件。此方法用于将树展开到指定的节点并选择它。这在 Page_Load
事件中非常方便,因为您可以将选项卡设置为当前页面
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();
}
关注点
DNN 安装说明
步骤 1:通过 DNN 安装模块
以 host 身份登录,并在 Host 菜单中选择“模块定义”。在页面底部,按“上传新模块”。然后,选择 .zip 文件,添加它,然后上传。
步骤 2:修改 web.config 文件(否则 DNN 将无法工作)
该模块是用 C# 编写的,您必须在 web.config 文件的 <system.web> <compilation>
部分中包含以下行(它应该已经存在,但被注释掉了)
<codeSubDirectories>
<add directoryName="DnnSiteMap"/>
</codeSubDirectories>
TreeNode 类的 Value 属性
由于 DNN 中的每个选项卡(页面)都有一个 TabId
,所以我将该信息与每个 TreeNode
一起保存。为此,我使用 value
字段。它的类型是 string
,所以,我会在需要时将其转换为 int
。
ExtendedNode 类
ExtendedNode
类直接派生自 System.Web.UI.WebControls.TreeNode
类。它为该类添加了布尔字段 HasChildren
。当我从数据库中检索节点时,如果它们有子节点,我就会将该标志设置为 true
。通过这个小技巧,我可以节省一次额外的数据库往返。
DummyNodes
TreeView
控件在节点至少有一个子节点时显示一个加号以展开节点。因为我不想在父节点展开时才检索节点,所以我用 DummyNode
填充它们。为了与普通节点区分开来,我将其 value
字段设置为 -1
。
ConfigSettings
我将设置封装在 ConfigurationSettings
类中。它从 Hashtable
(例如,ModuleSettingsBase.TabModuleSettings
或 PortalModuleBase.Settings
)中读取设置,使它们类型安全,并用默认值初始化它们。
路线图
新功能可以是:使用存储过程;定义自定义 CSS 类 .... 所以,还有很多工作要做。
Copyright
DNNSiteMap, 版权所有 (c) 2006 BitConstruct, Halanek & Co KEG (BitConstruct)。
特此免费授予任何获得本软件及相关文档文件(“软件”)副本的人处理本软件的权利,不受限制,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本的权利,并允许接收软件的人这样做,但须遵守以下条件
上述版权声明和本许可声明应包含在软件的所有副本或重要部分中。
本软件“按原样”提供,不附带任何明示或暗示的保证,包括但不限于适销性、特定用途适用性和非侵权性的保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担责任,无论是因合同、侵权或其他原因引起的,与软件或软件的使用或其他交易有关或由此产生的。
历史
- 2006/01/08 - V 0.1
- 概念验证,不适用于生产环境 (!)。
- 2006/01/13 - V 1.0
- 工作版本,包含许多新功能(仅限 DNN 4 和 ASP.NET 2.0)。
- 2006/01/28 - V 1.0.1
- 小错误修复:现在使用数据库对象的
ObjectQualifier
。
- 小错误修复:现在使用数据库对象的
- 2006/04/24 - V 1.0.2
- 新标志,仅显示用户有权限查看的选项卡。
- 可以关闭突出显示当前节点。
- 2006/05/07 - V 1.0.3
- 如果主机设置中启用了友好 URL,DNNSitemap 现在使用友好 URL。