如何使用c#创建递归的对象菜单层次结构

本文关键字:对象 菜单 层次结构 递归 何使用 创建 | 更新日期: 2023-09-27 18:01:31

对于一个项目,我想使用c#创建递归的对象(菜单)层次结构。我从互联网上阅读和尝试了很多,但我不能很快解决它。为了澄清我的问题,我创建了一个简单的数据库表。

CREATE TABLE Page
(
    Id INT NOT NULL PRIMARY KEY IDENTITY,
    ParentId INT DEFAULT 0,
    MenuTitle VARCHAR(255) NOT NULL
)
INSERT INTO Page (ParentId, MenuTitle) VALUES
(0, 'Parent 1'),
(1, 'Child 1'),
(1, 'Child 2'),
(3, 'ChildChild 1'),
(3, 'ChildChild 2'),
(3, 'ChildChild 3'),
(0, 'Parent 2'),
(0, 'Parent 3'),
(0, 'Parent 4')

*ParentId 0是一个主导航项

这是模型。

class PageItem
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public string MenuText { get; set; }
    List<PageItem> Childs { get; set; }
}

要从数据库中加载数据,可以使用这个方法。

public List<PageItem> GetPageItems()
        {
            List<PageItem> pageItems = new List<PageItem>();
            SqlConnection conn = new SqlConnection(" * YOUR CONNECTIONSTRING *");
            SqlCommand cmd = new SqlCommand("SELECT Id, ParentId, MenuTitle FROM Page", conn);
            conn.Open();
            SqlDataReader rdr = cmd.ExecuteReader();
            while (rdr.Read())
            {
                pageItems.Add(new PageItem()
                {
                    Id = Convert.ToInt32(rdr["Id"]),
                    ParentId = Convert.ToInt32(rdr["ParentId"]),
                    MenuText = rdr["MenuTitle"].ToString()
                });
            }
            rdr.Close();
            conn.Close();
            return pageItems;
        }

我希望模型是递归填充的,所以它看起来像这样。

- Parent 1
-- Child 1
-- Child 2
--- ChildChild 1
--- ChildChild 2
--- ChildChild 3
- Parent 2
- Parent 3
- Parent 4

有人知道我是怎么意识到这一点的吗?

谢谢你。

Jordy

如何使用c#创建递归的对象菜单层次结构

将读取循环替换为:

var allItems = new List<PageItem>();
while (rdr.Read())
{
   var item = new PageItem()
              {
                  Id = Convert.ToInt32(rdr["Id"]),
                  ParentId = Convert.ToInt32(rdr["ParentId"]),
                  MenuText = rdr["MenuTitle"].ToString()
              });
   allItems.Add(item);
   var parent = allItems.Where(pi => pi.Id == item.ParentId).SingleOrDefault();
   if (parent == null)
   {
      pageItems.Add(item);
   }
   else
   {
      if (parent.Childs == null)
         parent.Childs = new List<PageItem>();
      parent.Childs.Add(item);
   }
}

对于较大的数据集,使用Dictionary可以提供更好的性能。它只需要遍历所有可能的子节点一次并且在字典中查找父节点的次数接近于0 (1)这段代码依赖于数据库中经过适当清理的值。

Dictionary<int, PageItem> Items = new Dictionary<int, PageItem>();
while (rdr.Read())
{
    var item = new PageItem()
    {
        Id = Convert.ToInt32(rdr["Id"]),
        ParentId = Convert.ToInt32(rdr["ParentId"]),
        MenuText = rdr["MenuTitle"].ToString(),
        Childs = new List<PageItem>()
    };
    Items[item.Id] = item;
}
foreach (var pair in Items)
{
    PageItem item = pair.Value;
    if (item.ParentId == 0)
        continue;
    Items[item.ParentId].Childs.Add(item);
}

在返回pageItems之前,迭代每个项并使用具有它作为父项的项填充Childs集合…

using System.Linq;
// ...
pageItems.ForEach(p => 
{
    p.Childs = pagesItems.Where(c => c.ParentId == p.Id).ToList();
});
return pageItems;

不需要任何代码。你可以像这样使用common table expression

;with cte as 
(
    select convert(varchar(20),'-') as caption, *, convert(varchar(10),id) as o 
    from Page 
    where ParentId=0
    union all
    select convert(varchar(20),caption + '-'), page.id, page.parentid, page.menutitle, convert(varchar(10),o + convert(varchar(10),page.id) )
    from cte
        inner join page on cte.Id = page.ParentId    
)    
select caption+' ' + MenuTitle from cte
order by o