Wil*_*ill 10 c# sql asp.net tree hierarchy
我已经查看了一些SQL层次结构教程,但它们对我的应用程序都没有多大意义.也许我只是没有正确理解它们.我正在编写一个C#ASP.NET应用程序,我想从SQL数据创建一个树视图层次结构.
这是层次结构的工作方式:
SQL TABLE ID | Location ID | Name _______| __________ |_____________ 1331 | 1331 | House 1321 | 1331 | Room 2141 | 1321 | Bed 1251 | 2231 | Gym
如果ID和位置ID相同,这将决定顶级父级.该父母的任何子女都将拥有与父母相同的位置ID.该孩子的任何孙子女的位置ID都等于孩子的ID,依此类推.
对于上面的例子:
- House
-- Room
--- Bed
任何帮助或指导易于遵循的教程将不胜感激.
编辑:
我到目前为止的代码,但只有父母和孩子,没有GrandChildren.我似乎无法弄清楚如何让它以递归方式获取所有节点.
using System;
using System.Data;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.Data.SqlClient;
namespace TreeViewProject
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
PopulateTree(SampleTreeView);
}
public void PopulateTree(Control ctl)
{
// Data Connection
SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["AssetWhereConnectionString1"].ConnectionString);
connection.Open();
// SQL Commands
string getLocations = "SELECT ID, LocationID, Name FROM dbo.Locations";
SqlDataAdapter adapter = new SqlDataAdapter(getLocations, connection);
DataTable locations = new DataTable();
// Fill Data Table with SQL Locations Table
adapter.Fill(locations);
// Setup a row index
DataRow[] myRows;
myRows = locations.Select();
// Create an instance of the tree
TreeView t1 = new TreeView();
// Assign the tree to the control
t1 = (TreeView)ctl;
// Clear any exisiting nodes
t1.Nodes.Clear();
// BUILD THE TREE!
for (int p = 0; p < myRows.Length; p++)
{
// Get Parent Node
if ((Guid)myRows[p]["ID"] == (Guid)myRows[p]["LocationID"])
{
// Create Parent Node
TreeNode parentNode = new TreeNode();
parentNode.Text = (string)myRows[p]["Name"];
t1.Nodes.Add(parentNode);
// Get Child Node
for (int c = 0; c < myRows.Length; c++)
{
if ((Guid)myRows[p]["LocationID"] == (Guid)myRows[c]["LocationID"]
&& (Guid)myRows[p]["LocationID"] != (Guid)myRows[c]["ID"] /* Exclude Parent */)
{
// Create Child Node
TreeNode childNode = new TreeNode();
childNode.Text = (string)myRows[c]["Name"];
parentNode.ChildNodes.Add(childNode);
}
}
}
}
// ALL DONE BUILDING!
// Close the Data Connection
connection.Close();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是来自实际SQL表的snippit:Locations
ID LocationID Name ____________________________________ ____________________________________ ______________ DEAF3FFF-FD33-4ECF-910B-1B07DF192074 48700BC6-D422-4B26-B123-31A7CB704B97 Drop F 48700BC6-D422-4B26-B123-31A7CB704B97 7EBDF61C-3425-46DB-A4D5-686E91FD0832 Olway 06B49351-6D18-4595-8228-356253CF45FF 6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0 Drop E 5 E98BC1F6-4BAE-4022-86A5-43BBEE2BA6CD DEAF3FFF-FD33-4ECF-910B-1B07DF192074 Drop F 6 F6A2CF99-F708-4C61-8154-4C04A38ADDC6 7EBDF61C-3425-46DB-A4D5-686E91FD0832 Pree 0EC89A67-D74A-4A3B-8E03-4E7AAAFEBE51 6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0 Drop E 4 35540B7A-62F9-487F-B65B-4EA5F42AD88A 48700BC6-D422-4B26-B123-31A7CB704B97 Olway Breakdown 5000AB9D-EB95-48E3-B5C0-547F5DA06FC6 6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0 Out 1 53CDD540-19BC-4BC2-8612-5C0663B7FDA5 6E8C65AC-CB22-42DA-89EB-D81C5ED0BBD0 Drop E 3 7EBDF61C-3425-46DB-A4D5-686E91FD0821 B46C7305-18B1-4499-9E1C-7B6FDE786CD6 TEST 1 7EBDF61C-3425-46DB-A4D5-686E91FD0832 7EBDF61C-3425-46DB-A4D5-686E91FD0832 HMN
谢谢.
Chr*_*sen 15
您正在寻找使用公用表表达式的递归查询,或简称CTE.可以在MSDN上找到 SQL Server 2008中对此的详细说明.
通常,它们具有类似于以下的结构:
WITH cte_name ( column_name [,...n] )
AS (
–- Anchor
CTE_query_definition
UNION ALL
–- Recursive portion
CTE_query_definition
)
-- Statement using the CTE
SELECT * FROM cte_name
Run Code Online (Sandbox Code Playgroud)
执行此操作时,SQL Server将执行类似于以下操作(从MSDN转换为更简单的语言):
对于这个具体的例子,尝试这样的事情:
With hierarchy (id, [location id], name, depth)
As (
-- selects the "root" level items.
Select ID, [LocationID], Name, 1 As depth
From dbo.Locations
Where ID = [LocationID]
Union All
-- selects the descendant items.
Select child.id, child.[LocationID], child.name,
parent.depth + 1 As depth
From dbo.Locations As child
Inner Join hierarchy As parent
On child.[LocationID] = parent.ID
Where child.ID != parent.[Location ID])
-- invokes the above expression.
Select *
From hierarchy
Run Code Online (Sandbox Code Playgroud)
根据您的示例数据,您应该得到以下内容:
ID | Location ID | Name | Depth
_______| __________ |______ | _____
1331 | 1331 | House | 1
1321 | 1331 | Room | 2
2141 | 1321 | Bed | 3
Run Code Online (Sandbox Code Playgroud)
请注意,"健身房"不包括在内.根据您的示例数据,它的ID与[位置ID]不匹配,因此它不是根级别的项目.它的位置ID 2231未出现在有效父ID列表中.
您已经询问过如何将其转换为C#数据结构.在C#中表示层次结构的方法有很多种.这是一个例子,因其简单而选择.毫无疑问,真正的代码示例会更广泛.
第一步是定义层次结构中每个节点的外观.除了包含节点中的每个数据的特性,我已经包含Parent和Children性能,再加上方法来Add一个孩子和Get一个孩子.该Get方法将搜索节点的整个后代轴,而不仅仅是节点自己的子节点.
public class LocationNode {
public LocationNode Parent { get; set; }
public List<LocationNode> Children = new List<LocationNode>();
public int ID { get; set; }
public int LocationID { get; set; }
public string Name { get; set; }
public void Add(LocationNode child) {
child.Parent = this;
this.Children.Add(child);
}
public LocationNode Get(int id) {
LocationNode result;
foreach (LocationNode child in this.Children) {
if (child.ID == id) {
return child;
}
result = child.Get(id);
if (result != null) {
return result;
}
}
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
现在你想要填充你的树.这里有一个问题:以错误的顺序填充树很困难.在添加子节点之前,您确实需要对父节点的引用.如果必须按顺序执行此操作,则可以通过两次传递(一个用于创建所有节点,另一个用于创建树)来缓解问题.但是,在这种情况下,这是不必要的.
如果您使用上面提供的SQL查询并按depth列排序,则可以在数学上确定在遇到其父节点之前永远不会遇到子节点.因此,您可以一次完成此操作.
您仍然需要一个节点作为树的"根".您可以决定这是否为"House"(来自您的示例),或者它是否是您为此目的创建的虚构占位符节点.我建议后来.
那么,代码!同样,这是为了简化和可读性而优化的.您可能希望在生产代码中解决一些性能问题(例如,不必经常查找"父"节点).我在这里避免了这些优化,因为它们增加了复杂性.
// Create the root of the tree.
LocationNode root = new LocationNode();
using (SqlCommand cmd = new SqlCommand()) {
cmd.Connection = conn; // your connection object, not shown here.
cmd.CommandText = "The above query, ordered by [Depth] ascending";
cmd.CommandType = CommandType.Text;
using (SqlDataReader rs = cmd.ExecuteReader()) {
while (rs.Read()) {
int id = rs.GetInt32(0); // ID column
var parent = root.Get(id) ?? root;
parent.Add(new LocationNode {
ID = id,
LocationID = rs.GetInt32(1),
Name = rs.GetString(2)
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
当当!该rootLocationNode现在包含您的整个层次.顺便说一下,我还没有真正执行过这段代码,所以如果你发现任何明显的问题,请告诉我.
要修复示例代码,请进行以下更改:
删除此行:
// Create an instance of the tree
TreeView t1 = new TreeView();
Run Code Online (Sandbox Code Playgroud)
这一行实际上不是问题,但应删除.你在这里的评论是不准确的; 你并没有真正为控件分配树.相反,您正在创建一个新的TreeView,将其分配给t1,然后立即分配一个不同的对象t1.下一行执行后,您创建的TreeView将丢失.
修复您的SQL语句
// SQL Commands
string getLocations = "SELECT ID, LocationID, Name FROM dbo.Locations";
Run Code Online (Sandbox Code Playgroud)
使用ORDER BY子句将此SQL语句替换为我之前建议的SQL语句.阅读我之前的编辑,解释为什么"深度"很重要:您确实想要按特定顺序添加节点.在拥有父节点之前,无法添加子节点.
或者,我认为您不需要这里的SqlDataAdapter和DataTable的开销.我最初建议的DataReader解决方案更简单,更易于使用,并且在资源方面更有效.
此外,大多数C#SQL对象都会实现IDisposable,因此您需要确保正确使用它们.如果有什么实现IDisposable,请确保将其包装在using语句中(请参阅我之前的C#代码示例).
修复树木构建循环
您只获取父节点和子节点,因为您有父节点循环和子节点内循环.正如你必须已经知道的那样,你没有得到孙子孙女,因为你没有添加它们的代码.
你可以添加一个内在循环来获得孙子孙女,但显然你是在寻求帮助,因为你已经意识到这样做只会导致疯狂.如果你想要曾孙子会怎么样?内 - 内 - 内环?这种技术不可行.
你可能想过这里的递归.这是一个完美的地方,如果你正在处理树状结构,它最终会出现.既然您已经编辑了问题,很明显您的问题几乎与SQL无关.你的真正问题在于递归.有人可能会最终出现并为此设计一个递归解决方案.这将是一个完全有效的,可能更好的方法.
但是,我的答案已经涵盖了递归部分 - 它只是将其移动到SQL层.因此,我将保留以前的代码,因为我觉得这是一个合适的通用答案.根据您的具体情况,您需要进行一些修改.
首先,你不需要LocationNode我建议的课程.你正在使用TreeNode,这将工作正常.
其次,TreeView.FindNode它类似于LocationNode.Get我建议的方法,除了FindNode需要到节点的完整路径.要使用FindNode,您必须修改SQL以提供此信息.
因此,您的整个PopulateTree函数应如下所示:
public void PopulateTree(TreeView t1) {
// Clear any exisiting nodes
t1.Nodes.Clear();
using (SqlConnection connection = new SqlConnection()) {
connection.ConnectionString = "((replace this string))";
connection.Open();
string getLocations = @"
With hierarchy (id, [location id], name, depth, [path])
As (
Select ID, [LocationID], Name, 1 As depth,
Cast(Null as varChar(max)) As [path]
From dbo.Locations
Where ID = [LocationID]
Union All
Select child.id, child.[LocationID], child.name,
parent.depth + 1 As depth,
IsNull(
parent.[path] + '/' + Cast(parent.id As varChar(max)),
Cast(parent.id As varChar(max))
) As [path]
From dbo.Locations As child
Inner Join hierarchy As parent
On child.[LocationID] = parent.ID
Where child.ID != parent.[Location ID])
Select *
From hierarchy
Order By [depth] Asc";
using (SqlCommand cmd = new SqlCommand(getLocations, connection)) {
cmd.CommandType = CommandType.Text;
using (SqlDataReader rs = cmd.ExecuteReader()) {
while (rs.Read()) {
// I guess you actually have GUIDs here, huh?
int id = rs.GetInt32(0);
int locationID = rs.GetInt32(1);
TreeNode node = new TreeNode();
node.Text = rs.GetString(2);
node.Value = id.ToString();
if (id == locationID) {
t1.Nodes.Add(node);
} else {
t1.FindNode(rs.GetString(4)).ChildNodes.Add(node);
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您发现任何其他错误,请告诉我们!