使用 HierarchyID 加载 TreeView






4.55/5 (9投票s)
使用 SQL Server 2008 Hierarchy ID 数据类型填充 TreeView

引言
本文演示了如何基于 SQL Server 2008 中的新 hierarchyID
数据类型加载 TreeView
。我以前加载 TreeView
的方法要么是使用具有数据关系的数据集,要么是使用具有父/子层次结构的数据表。
我发现您需要限制 TreeView
中的节点数量才能保持加载性能。使用 hierarchyid
,这个限制得到了提高。我对加载速度感到惊喜。
本文假设您已使用 SQL 2008 中的 hierarchyID
构建了结果集。该示例表以 XML 形式存储,具有现实的大小和复杂性,因为它基于经过清理的实际数据。构建结果集本身就是一个挑战,而且过于个性化,在此不予涵盖。
背景
MSDN 包含一些非常有用的示例和教程。
Using the Code
首先,您需要引用 Microsoft 类型,因为 hierarchyid
不是您所期望的 SQLDBType
。
using Microsoft.SqlServer.Types;
获取数据
再次,我将数据类拆分出来,并为此演示使用了 XML 示例数据文件。请注意,从文本系统加载数据时,hierarchyID
需要在 ID 有用之前从文本中进行转换。
//convert the string back into a hierarchyid
oRow["NodeKey"] = SqlHierarchyId.Parse((string)oRow["NodeString"]);
我将此数据加载到 datagridview
中,以便在加载 TreeView
之前检查内容。请注意 hierarchyID
的结构,它是用正斜杠分隔的 ID 值。

这里有一条关于在 SQL Server 中构建 hierarchyID
的说明,所有 Microsoft 的教程都使用 ID 号来指定层次结构级别内节点的顺序。这带来了问题,因为我需要从 UI 添加节点,而重复节点自然是不允许的,所以我必须在 UI 中构建节点时对 ID 进行一些处理。
我的解决方法是使用数据库中的 ID。我在 select
语句中使用顺序来排序显示,并且我没有要求在层次结构内的级别中排序数据。这也可以通过 LINQ 过滤器来实现,但我更熟悉 TSQL。
加载 TreeView
自然,hierachyID
函数不能在 dataview
过滤器或 datatable select
中使用,函数支持有限。所以,我需要找到另一种方法来识别父/子节点以加载到 TreeView
中。我看了看使用 NodeString
字段和 string.contains
以及计算分隔符的数量(这也有效)。
似乎微软在 SQL Server 中实现了 hierarchyid
却没有在 CLR 中实现,这似乎不对,毕竟您应该能够集成两者。所以,又花了几小时的搜索,找到了对 SQLTypes
的引用,从那里转向 LINQ 是显而易见的。
我将 LoadTreeSQLHierarchy
和 LoadNodeSQLHierarchy
构建成可以移动到实用类中的方式。
private void LoadTreeSQLHierarchy(TreeView oTV, DataTable oTable,
string sKeyField, string sTextField)
{
oTV.Nodes.Clear();
TreeNode oNode;
//get an empty id to get the top node
SqlHierarchyId iID = new SqlHierarchyId();
//filter the table using linq. See blog for equals()/== issue
EnumerableRowCollection<DataRow> query =
from TNodes in oTable.AsEnumerable()
where TNodes.Field<SqlHierarchyId>
(sKeyField).GetAncestor(1).Equals(iID)
select TNodes;
//convert to a dataview because I am comfortable with a dataview.
DataView oDV = query.AsDataView();
if (oDV.Count == 1)
{
//load up a node
oNode = new TreeNode(oDV[0][sTextField].ToString());
//put the datarow into the tag property
oNode.Tag = oDV[0].Row;
//load up the children
LoadNodeSQLHierarchy(oNode, oTable);
//add the node hierarchy to the tree
oTV.Nodes.Add(oNode);
}
}
通过使用空的 hierarchyID
和初始 LINQ 查询中的 GetAncestor(1)
,我们检索了顶级节点。这允许我们使用一种方法来加载树和节点,但我们需要处理 TreeView
和 TreeNode
的不同类型。我认为使用两种方法更简单。
我从 这里 获得了 .Equals()
的要求。
由于我对数据结构更熟悉,我使用 DataView
来填充 treeview
的顶级节点。一旦有了顶级节点,我就可以递归调用 LoadNodeSQLHierarchy
来填充整个结构。
使用 dataview
的另一个好处是,我可以将 datarow
放入节点的 tag
属性中,并在 UI 中处理树时可以使用它。
兴趣点
我非常不喜欢 hierarchyID
的地方在于它不是 SQLDBType
,因此打破了我基于存储过程和 SQLDBTypes
的 ORM 层。
历史
- 第一个版本