如何在 C# dotnet 中将 HierarchyId 转换为 Child/Parent (Tree) 结构?
How to convert HierarchyId to Child/Parent (Tree) structure in C# dotnet?
出于性能等原因,我必须在我的数据库中使用HiarachyId。我必须将 HierarchyId 数据类型转换为 JSON 才能显示在 FancyTree 中。
我使用解决方案here,但不起作用。我的代码是
static void Main(string[] args)
{
{
var dd = new List<Field>();
dd.Add(new Field(1, "Earth", HierarchyId.Parse("/")));
dd.Add(new Field(2, "Europe", HierarchyId.Parse("/1/")));
dd.Add(new Field(3, "South America", HierarchyId.Parse("/2/")));
dd.Add(new Field(4, "Antarctica", HierarchyId.Parse("/3/")));
dd.Add(new Field(5, "Brazil", HierarchyId.Parse("/2/1/")));
dd.Add(new Field(6, "France", HierarchyId.Parse("/1/1/")));
dd.Add(new Field(7, "Germany", HierarchyId.Parse("/1/4/")));
dd.Add(new Field(8, "test", HierarchyId.Parse("/1/5/")));
dd.Add(new Field(9, "McMurdo Station", HierarchyId.Parse("/3/1/")));
dd.Add(new Field(10, "Italy", HierarchyId.Parse("/1/3/")));
dd.Add(new Field(11, "Spain", HierarchyId.Parse("/1/2/")));
dd.Add(new Field(12, "Morano", HierarchyId.Parse("/1/3/1/")));
dd.Add(new Field(13, "Rio de Janeiro", HierarchyId.Parse("/2/1/3/")));
dd.Add(new Field(14, "Paris", HierarchyId.Parse("/1/1/1/")));
dd.Add(new Field(15, "Madrid", HierarchyId.Parse("/1/2/1/")));
dd.Add(new Field(16, "Brasilia", HierarchyId.Parse("/2/1/1/")));
dd.Add(new Field(17, "Bahia", HierarchyId.Parse("/2/1/2/")));
dd.Add(new Field(18, "Salvador", HierarchyId.Parse("/2/1/2/1/")));
dd.Add(new Field(19, "tets1", HierarchyId.Parse("/2/1/3/1/")));
dd.Add(new Field(20, "test2", HierarchyId.Parse("/2/1/3/1/1/")));
dd.Add(new Field(21, "test3", HierarchyId.Parse("/2/1/3/1/1/1/")));
dd.Add(new Field(22, "test24", HierarchyId.Parse("/2/1/3/1/1/2/")));
MyClass clss = new MyClass();
var x= clss.NewMthodTest(dd);
}
}
获取子方法:
public class MyClass
{
public List<HierarchicalNode> NewMthodTest(List<Field> query)
{
var root = new HierarchicalNode("Root", 0);
foreach (var rec in query)
{
var current = root;
foreach (string part in rec.Node.ToString().Split(new[] { '/' },
StringSplitOptions.RemoveEmptyEntries))
{
int parsedPart = int.Parse(part);
current = current.Children[parsedPart - 1];
}
current.Children.Add(new HierarchicalNode(rec.FieldName, rec.Id));
}
return null; // in this method i don't know what do we suppose to return
}
}
我的输入参数class是:
public class Field
{
public Field(long id, string fieldName, HierarchyId node)
{
Id = id;
FieldName = fieldName;
Node = node;
}
public long Id { get; set; }
public string FieldName { get; set; }
public HierarchyId Node { get; set; }
}
并且输出 class 是
class HierarchicalNode
{
private readonly List<HierarchicalNode> children =
new List<HierarchicalNode>();
public List<HierarchicalNode> Children { get { return children; } }
private readonly string name;
public string Name { get { return name; } }
private readonly long id;
public long Id { get { return id; } }
public HierarchicalNode(string name, long id)
{
this.name = name;
this.id = id;
}
}
似乎出了点问题,它 returns 是这样的:
网上找了很多无果,自己用c#的递归方法解决了
我把我的总代码放在这里是为了帮助以后搜索这个问题的人。如果有人有什么好的建议可以让它变得更好,请留言让我开心。
这是我的代码:
这是调用 GetTreeMethod 的 My Main 方法
public List<TreeView> GetTree()
{
//get all nodes from DB to make a collection in RAM
var nodesColect = _getFieldsList.GetFieldsByNode("/");
var x = GetTreeMethod("/", nodesColect);
return x;
}
这是我主要的递归方法
private List<TreeView> GetTreeMethod(string nodeStr,List<FieldListDto> lstCollection)
{
List<TreeView> lst = new List<TreeView>();
HierarchyId node = HierarchyId.Parse(nodeStr);
var lastItemInCurrentLevel = GetChilds(node, lstCollection);
foreach (var item in lastItemInCurrentLevel)
{
TreeView tr = new TreeView
{
title = item.title,
id = item.id,
node = item.node,
fin = item.fin,
};
tr.children = GetTreeMethod(item.node.ToString(), lstCollection);
lst.Add(tr);
}
return lst;
}
这只会给出特定节点的子节点
private List<TreeView> GetChilds(HierarchyId node, List<FieldListDto> lstCollection)
{
List<TreeView> child = lstCollection.Where(x => x.Node.ToString() != "/" && x.Node.GetAncestor(1).ToString() == node.ToString()).Select(q => new TreeView { id = q.Id, node = q.Node, title = q.FieldName }).ToList();
return child;
}
型号
public class FieldListDto
{
public long id { get; set; }
public string FieldName { get; set; }
public HierarchyId Node { get; set; }
}
public class TreeView
{
public long id { get; set; }
public string title { get; set; }
public HierarchyId node { get; set; }
public List<TreeView> children { get; set; }
}
这里是我的 SQL 数据
这是我的最终结果
使用 HierarchyId 的好处之一是您可以在不进行递归调用的情况下构建树。
我也会以不同的方式命名。假设您将数据库称为 table 个节点。这是 table 结构:
CREATE TABLE dbo.Nodes
(
[NodeId] [int] NOT NULL,
[NodeName] [nvarchar](100) NOT NULL,
[HierarchyId] [hierarchyid] NOT NULL,
[Level] AS [HierarchyId].GetLevel() PERSISTED,
CONSTRAINT Primary_Key_Nodes PRIMARY KEY CLUSTERED ([NodeId])
);
CREATE UNIQUE NONCLUSTERED INDEX
[Nodes_1]
ON
[dbo].[Nodes] ([HierarchyId] ASC);
--Important - put level as the first column to index
CREATE UNIQUE NONCLUSTERED INDEX
[Nodes_2]
ON
[dbo].[Nodes] ([Level] ASC, [HierarchyId] ASC);
这是给定父节点的 SQL 到 return 节点。我会将其包装在一个名为 GetDescendantsAndSelf():
的函数中
SELECT
[NodeId]
,[NodeName]
,[HierarchyId].ToString() AS 'HierarchyPath'
FROM
[dbo].[Nodes]
WHERE
[HierarchyId].IsDescendantOf(@parentHierarchyId) = 1
ORDER BY
[Level] ASC
,[NodeName] ASC;
我的数据传输对象可能如下所示:
public class TreeNode
{
public string Text { get; set; } = String.Empty;
public List<TreeNode> Nodes { get; set; } = new List<TreeNode>();
}
GetDescendantsAndSelf() 应该return 像这样的节点数据访问对象列表:
public class Node
{
public int NodeId { get; set; }
public string NodeName { get; set; } = String.Empty;
public SqlHierarchyId HierarchyId { get; set; }
public int Level => HierarchyId.GetLevel().ToSqlInt32().Value;
}
这里是构建树的代码:
TreeNode? rootNode = null;
Dictionary<string, TreeNode> treeBuilder = new Dictionary<string, TreeNode>();
string parentHierarchyId = "/1/2/3";
var nodes = GetDescendantsAndSelf(parentHierarchyId);
foreach (Node node in nodes)
{
TreeNode currentNode = new TreeNode() { Text = node.NodeName };
treeBuilder[node.HierarchyId.ToString()] = currentNode;
if (node.Level == 1)
{
rootNode = currentNode;
}
else
{
string parentKey = node.HierarchyId.GetAncestor(1).ToString();
treeBuilder[parentKey].Nodes.Add(currentNode);
}
}
if (rootNode is {})
{
//rootNode contains your tree structure
}
else
{
//no data found for parentHierarchyId
}
出于性能等原因,我必须在我的数据库中使用HiarachyId。我必须将 HierarchyId 数据类型转换为 JSON 才能显示在 FancyTree 中。
我使用解决方案here,但不起作用。我的代码是
static void Main(string[] args)
{
{
var dd = new List<Field>();
dd.Add(new Field(1, "Earth", HierarchyId.Parse("/")));
dd.Add(new Field(2, "Europe", HierarchyId.Parse("/1/")));
dd.Add(new Field(3, "South America", HierarchyId.Parse("/2/")));
dd.Add(new Field(4, "Antarctica", HierarchyId.Parse("/3/")));
dd.Add(new Field(5, "Brazil", HierarchyId.Parse("/2/1/")));
dd.Add(new Field(6, "France", HierarchyId.Parse("/1/1/")));
dd.Add(new Field(7, "Germany", HierarchyId.Parse("/1/4/")));
dd.Add(new Field(8, "test", HierarchyId.Parse("/1/5/")));
dd.Add(new Field(9, "McMurdo Station", HierarchyId.Parse("/3/1/")));
dd.Add(new Field(10, "Italy", HierarchyId.Parse("/1/3/")));
dd.Add(new Field(11, "Spain", HierarchyId.Parse("/1/2/")));
dd.Add(new Field(12, "Morano", HierarchyId.Parse("/1/3/1/")));
dd.Add(new Field(13, "Rio de Janeiro", HierarchyId.Parse("/2/1/3/")));
dd.Add(new Field(14, "Paris", HierarchyId.Parse("/1/1/1/")));
dd.Add(new Field(15, "Madrid", HierarchyId.Parse("/1/2/1/")));
dd.Add(new Field(16, "Brasilia", HierarchyId.Parse("/2/1/1/")));
dd.Add(new Field(17, "Bahia", HierarchyId.Parse("/2/1/2/")));
dd.Add(new Field(18, "Salvador", HierarchyId.Parse("/2/1/2/1/")));
dd.Add(new Field(19, "tets1", HierarchyId.Parse("/2/1/3/1/")));
dd.Add(new Field(20, "test2", HierarchyId.Parse("/2/1/3/1/1/")));
dd.Add(new Field(21, "test3", HierarchyId.Parse("/2/1/3/1/1/1/")));
dd.Add(new Field(22, "test24", HierarchyId.Parse("/2/1/3/1/1/2/")));
MyClass clss = new MyClass();
var x= clss.NewMthodTest(dd);
}
}
获取子方法:
public class MyClass
{
public List<HierarchicalNode> NewMthodTest(List<Field> query)
{
var root = new HierarchicalNode("Root", 0);
foreach (var rec in query)
{
var current = root;
foreach (string part in rec.Node.ToString().Split(new[] { '/' },
StringSplitOptions.RemoveEmptyEntries))
{
int parsedPart = int.Parse(part);
current = current.Children[parsedPart - 1];
}
current.Children.Add(new HierarchicalNode(rec.FieldName, rec.Id));
}
return null; // in this method i don't know what do we suppose to return
}
}
我的输入参数class是:
public class Field
{
public Field(long id, string fieldName, HierarchyId node)
{
Id = id;
FieldName = fieldName;
Node = node;
}
public long Id { get; set; }
public string FieldName { get; set; }
public HierarchyId Node { get; set; }
}
并且输出 class 是
class HierarchicalNode
{
private readonly List<HierarchicalNode> children =
new List<HierarchicalNode>();
public List<HierarchicalNode> Children { get { return children; } }
private readonly string name;
public string Name { get { return name; } }
private readonly long id;
public long Id { get { return id; } }
public HierarchicalNode(string name, long id)
{
this.name = name;
this.id = id;
}
}
似乎出了点问题,它 returns 是这样的:
网上找了很多无果,自己用c#的递归方法解决了
我把我的总代码放在这里是为了帮助以后搜索这个问题的人。如果有人有什么好的建议可以让它变得更好,请留言让我开心。
这是我的代码:
这是调用 GetTreeMethod 的 My Main 方法
public List<TreeView> GetTree()
{
//get all nodes from DB to make a collection in RAM
var nodesColect = _getFieldsList.GetFieldsByNode("/");
var x = GetTreeMethod("/", nodesColect);
return x;
}
这是我主要的递归方法
private List<TreeView> GetTreeMethod(string nodeStr,List<FieldListDto> lstCollection)
{
List<TreeView> lst = new List<TreeView>();
HierarchyId node = HierarchyId.Parse(nodeStr);
var lastItemInCurrentLevel = GetChilds(node, lstCollection);
foreach (var item in lastItemInCurrentLevel)
{
TreeView tr = new TreeView
{
title = item.title,
id = item.id,
node = item.node,
fin = item.fin,
};
tr.children = GetTreeMethod(item.node.ToString(), lstCollection);
lst.Add(tr);
}
return lst;
}
这只会给出特定节点的子节点
private List<TreeView> GetChilds(HierarchyId node, List<FieldListDto> lstCollection)
{
List<TreeView> child = lstCollection.Where(x => x.Node.ToString() != "/" && x.Node.GetAncestor(1).ToString() == node.ToString()).Select(q => new TreeView { id = q.Id, node = q.Node, title = q.FieldName }).ToList();
return child;
}
型号
public class FieldListDto
{
public long id { get; set; }
public string FieldName { get; set; }
public HierarchyId Node { get; set; }
}
public class TreeView
{
public long id { get; set; }
public string title { get; set; }
public HierarchyId node { get; set; }
public List<TreeView> children { get; set; }
}
这里是我的 SQL 数据
使用 HierarchyId 的好处之一是您可以在不进行递归调用的情况下构建树。
我也会以不同的方式命名。假设您将数据库称为 table 个节点。这是 table 结构:
CREATE TABLE dbo.Nodes
(
[NodeId] [int] NOT NULL,
[NodeName] [nvarchar](100) NOT NULL,
[HierarchyId] [hierarchyid] NOT NULL,
[Level] AS [HierarchyId].GetLevel() PERSISTED,
CONSTRAINT Primary_Key_Nodes PRIMARY KEY CLUSTERED ([NodeId])
);
CREATE UNIQUE NONCLUSTERED INDEX
[Nodes_1]
ON
[dbo].[Nodes] ([HierarchyId] ASC);
--Important - put level as the first column to index
CREATE UNIQUE NONCLUSTERED INDEX
[Nodes_2]
ON
[dbo].[Nodes] ([Level] ASC, [HierarchyId] ASC);
这是给定父节点的 SQL 到 return 节点。我会将其包装在一个名为 GetDescendantsAndSelf():
的函数中SELECT
[NodeId]
,[NodeName]
,[HierarchyId].ToString() AS 'HierarchyPath'
FROM
[dbo].[Nodes]
WHERE
[HierarchyId].IsDescendantOf(@parentHierarchyId) = 1
ORDER BY
[Level] ASC
,[NodeName] ASC;
我的数据传输对象可能如下所示:
public class TreeNode
{
public string Text { get; set; } = String.Empty;
public List<TreeNode> Nodes { get; set; } = new List<TreeNode>();
}
GetDescendantsAndSelf() 应该return 像这样的节点数据访问对象列表:
public class Node
{
public int NodeId { get; set; }
public string NodeName { get; set; } = String.Empty;
public SqlHierarchyId HierarchyId { get; set; }
public int Level => HierarchyId.GetLevel().ToSqlInt32().Value;
}
这里是构建树的代码:
TreeNode? rootNode = null;
Dictionary<string, TreeNode> treeBuilder = new Dictionary<string, TreeNode>();
string parentHierarchyId = "/1/2/3";
var nodes = GetDescendantsAndSelf(parentHierarchyId);
foreach (Node node in nodes)
{
TreeNode currentNode = new TreeNode() { Text = node.NodeName };
treeBuilder[node.HierarchyId.ToString()] = currentNode;
if (node.Level == 1)
{
rootNode = currentNode;
}
else
{
string parentKey = node.HierarchyId.GetAncestor(1).ToString();
treeBuilder[parentKey].Nodes.Add(currentNode);
}
}
if (rootNode is {})
{
//rootNode contains your tree structure
}
else
{
//no data found for parentHierarchyId
}