【问题标题】:ASP.Net MVC: How to show nested parent-child relation using recursive techniqueASP.Net MVC:如何使用递归技术显示嵌套的父子关系
【发布时间】:2018-03-27 01:45:54
【问题描述】:

我只是尝试使用递归函数调用在剃须刀中使用ulli 显示第n 个关系。假设我有一个存储父子关系的数据库表,如下所示。

表结构

+----+----------+----------+
| ID | Name     | ParentID |
+----+----------+----------+
| 1  | Parent 1 | 0        |
+----+----------+----------+
| 2  | child 1  | 1        |
+----+----------+----------+
| 3  | child 2  | 1        |
+----+----------+----------+
| 4  | child 3  | 1        |
+----+----------+----------+
| 5  | Parent   | 0        |
+----+----------+----------+
| 6  | child 4  | 4        |
+----+----------+----------+

所以我喜欢在 razor 视图中以这种方式显示嵌套数据

Parent 1
    child 1
    child 2
    child 3
        child 4
Parent

所以我尝试了这段代码但无法实现目标。

c# POCO 类

public class MenuItem 
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
    public virtual ICollection<MenuItem> Children { get; set; }
}

public class MenuDTO 
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
    public virtual ICollection<MenuItem> Children { get; set; }
}

操作代码

    public ActionResult Index()
    {

        List<MenuItem> allMenu = new List<MenuItem>
        {
            new MenuItem {Id=1,Name="Parent 1", ParentId=0},
            new MenuItem {Id=2,Name="child 1", ParentId=1},
            new MenuItem {Id=3,Name="child 2", ParentId=1},
            new MenuItem {Id=4,Name="child 3", ParentId=1},
            new MenuItem {Id=5,Name="Parent 2", ParentId=0},
            new MenuItem {Id=6,Name="child 4", ParentId=4}
        };


        List<MenuDTO> mi = allMenu
        .Select(e => new
        {
            Id = e.Id,
            Name = e.Name,
            ParentId = e.ParentId,
            Children = allMenu.Where(x => x.ParentId == e.Id).ToList()
        }).ToList()
        .Select(p => new MenuDTO
        {
            Id = p.Id,
            Name = p.Name,
            ParentId = p.ParentId,
            Children = p.Children
            //Children = p.Children.Cast<MenuDTO>()
        }).ToList();

        ViewBag.menusList = mi;

        return View();
    }

剃刀代码

@{
    var menuList = ViewBag.menusList as List<Scaffolding.Controllers.MenuDTO>;
    ShowTree(menuList);
}


@helper ShowTree(List<Scaffolding.Controllers.MenuDTO> menusList)
{
    if (menusList != null)
    {
        foreach (var item in menusList)
        {
            <li>
                <span>@item.Name</span>
                @if (item.Children.Any())
                {
                    <ul>
                        @ShowTree(item.Children)
                    </ul>
                }
            </li>
        }
    }
}

在我的情况下,我查询 List allMenu 以从 db 表中获取数据。当我运行我的代码时,我遇到了错误

CS1502:最佳重载方法匹配 'ASP._Page_Views_Menu_Index_cshtml.ShowTree(System.Collections.Generic.List)' 有一些无效的参数

告诉我我的代码有什么问题。请帮助我解决并实现我的目标。谢谢

编辑

完整的工作代码如下

@helper ShowTree(List<Scaffolding.Controllers.MenuItem> menusList)
{
    <ul>
        @foreach (var item in menusList)
        {
            <li>
                <span>@item.Name</span>
                @if (item.Children!=null && item.Children.Any())
                {
                    @ShowTree(item.Children)
                }
            </li>
        }
    </ul>
}

@{
    var menuList = ViewBag.menusList as List<Scaffolding.Controllers.MenuItem>;
    @ShowTree(menuList);
}

public ActionResult Index()
{

    List<MenuItem> allMenu = new List<MenuItem>
    {
        new MenuItem {Id=1,Name="Parent 1", ParentId=0},
        new MenuItem {Id=2,Name="child 1", ParentId=1},
        new MenuItem {Id=3,Name="child 2", ParentId=1},
        new MenuItem {Id=4,Name="child 3", ParentId=1},
        new MenuItem {Id=5,Name="Parent 2", ParentId=0},
        new MenuItem {Id=6,Name="child 4", ParentId=4}
    };

    List<MenuItem> mi = allMenu
    .Where(e => e.ParentId == 0) /* grab only the root parent nodes */
    .Select(e => new MenuItem
    {
        Id = e.Id,
        Name = e.Name,
        ParentId = e.ParentId,
        Children = allMenu.Where(x => x.ParentId == e.Id)  /* grab second level children */
            .Select(e2 => new MenuItem
            {
                Id = e2.Id,
                Name = e2.Name,
                ParentId = e2.ParentId,
                Children = allMenu.Where(x2 => x2.ParentId == e2.Id).ToList() /* grab third level children */
            }).ToList()
    }).ToList();

    ViewBag.menusList = mi;

    return View();
}

public class MenuItem 
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
    public virtual List<MenuItem> Children { get; set; }
}

【问题讨论】:

  • ShowTree 需要一个列表,但您正在发送项目 @ShowTree(item.Children) 。如果您将@ShowTree(item.Children) 替换为@ShowTree(item),它会将列表传递给方法。

标签: c# asp.net-mvc-4


【解决方案1】:

我以这种方式修复并完成了工作

问题出在剃须刀代码的逻辑上,我也评论了这一行 //.Where(e =&gt; e.ParentId == 0) 这里我正在添加工作代码。

工作代码示例

@helper  ShowTree(List<NestedChild.Controllers.MenuItem> menu, int? parentid = 0, int level = 0)
{
    var items = menu.Where(m => m.ParentId == parentid);

    if (items.Any())
    {
        if (items.First().ParentId > 0)
        {
            level++;
        }

        <ul>
            @foreach (var item in items)
            {
            <li>
                @item.Name
            </li>
                @ShowTree(menu, item.Id, level);
            }
        </ul>
    }
}
@{
    var menuList = ViewBag.menusList as List<NestedChild.Controllers.MenuItem>;
    @ShowTree(menuList);
}

public ActionResult Index()
{
    List<MenuItem> allMenu = new List<MenuItem>
    {
        new MenuItem {Id=1,Name="Parent 1", ParentId=0},
        new MenuItem {Id=2,Name="child 1", ParentId=1},
        new MenuItem {Id=3,Name="child 2", ParentId=1},
        new MenuItem {Id=4,Name="child 3", ParentId=1},
        new MenuItem {Id=5,Name="Parent 2", ParentId=0},
        new MenuItem {Id=6,Name="child 4", ParentId=4}
    };

    List<MenuItem> mi = allMenu
    //.Where(e => e.ParentId == 0) /* grab only the root parent nodes */
    .Select(e => new MenuItem
    {
        Id = e.Id,
        Name = e.Name,
        ParentId = e.ParentId,
        //Children = allMenu.Where(x => x.ParentId == e.Id).ToList()
    }).ToList();

    ViewBag.menusList = mi;

    return View();
}

【讨论】:

  • 看起来很棒!我更喜欢你的解决方案。如果列表来自数据库,我的代码会更慢,因为它为每组子项对数据库进行更多的往返。您的代码会在获得结果后通过层次结构遍历的权衡进行一次查询,这应该会更快。
  • 仅供参考,您不再需要 Children = allMenu.Where(x =&gt; x.ParentId == e.Id).ToList() 行或 Children 属性。它只会减慢你的代码。
  • 是的,你是对的。我会评论那些行。感谢您的帮助和支持。
【解决方案2】:

您的代码在执行该行时找不到采用 ICollection&lt;MenuItem&gt; 类型参数的函数 ShowTree

@ShowTree(item.Children)

因为 item.Children 的类型为 ICollection&lt;MenuItem&gt;。代码中的函数 ShowTree 采用不同类型的参数 List&lt;MenuDTO&gt;,它与 ICollection&lt;MenuItem&gt; 不同。因此,运行时会报告您看到的 CS1502 错误。

在意识到您正在寻找递归解决方案后,我已经修改了代码来做到这一点。

操作代码

public ActionResult Index()
{

    List<MenuItem> allMenu = new List<MenuItem>
    {
        new MenuItem {Id=1,Name="Parent 1", ParentId=0},
        new MenuItem {Id=2,Name="child 1", ParentId=1},
        new MenuItem {Id=3,Name="child 2", ParentId=1},
        new MenuItem {Id=4,Name="child 3", ParentId=1},
        new MenuItem {Id=5,Name="Parent 2", ParentId=0},
        new MenuItem {Id=6,Name="child 4", ParentId=4}
    };

    List<MenuItem> mi = allMenu
    .Where(e => e.ParentId == 0) /* grab only the root parent nodes */
    .Select(e => new MenuItem
    {
        Id = e.Id,
        Name = e.Name,
        ParentId = e.ParentId,
        Children = GetChildren(allMenu, e.Id) /* Recursively grab the children */
    }).ToList();

    ViewBag.menusList = mi;

    return View();
}

/// <summary>
/// Recursively grabs the children from the list of items for the provided parentId
/// </summary>
/// <param name="items">List of all items</param>
/// <param name="parentId">Id of parent item</param>
/// <returns>List of children of parentId</returns>
private static List<MenuItem> GetChildren(List<MenuItem> items, int parentId)
{
    return items
        .Where(x => x.ParentId == parentId)
        .Select(e => new MenuItem
        {
            Id = e.Id,
            Name = e.Name,
            ParentId = e.ParentId,
            Children = GetChildren(items, e.Id)
        }).ToList();
}

剃刀密码

@{
    var menuList = ViewBag.menusList as List<Scaffolding.Controllers.MenuItem>;
    @ShowTree(menuList);
}

@helper ShowTree(List<Scaffolding.Controllers.MenuItem> menusList)
{
    if (menusList != null)
    {
        foreach (var item in menusList)
        {
            <li>
                <span>@item.Name</span>
                @if (item.Children.Any())
                {
                    <ul>
                        @ShowTree(item.Children)
                    </ul>
                }
            </li>
        }
    }
}

【讨论】:

  • 您的代码中有一个错误是 ShowTree(menuList);应该是@ShowTree(menuList);然后将调用 ShowTree 并在页面中写入数据。不管怎样,谢谢你的回答。
  • 好收获!抱歉,我没有机会测试代码。 我只是想让您知道该算法可能遗漏了 child 4。如果您知道将恰好有三层菜单项,那么您可以为当前Select 子句的Children 属性添加另一层Where(...).Select {...}.ToList() 子句以覆盖第三层菜单(其中 child 4 坐)。
  • 请查看上面的代码,告诉我要更改哪些内容,因此应该显示嵌套的子项。当我运行我的程序时,孩子 4 的名字没有显示哪个是孩子 3 的孩子。代码中有什么问题,如果你知道,请告诉我要解决的问题。谢谢
  • 我继续修改了您的“完整工作代码”中的查询。我是新来的贡献者,所以我希望你不要介意。
  • 我终于意识到我错过了您想要的递归查询部分,我已将其添加到我的解决方案中。请使用新更改测试并更新您的“完整工作代码”。
猜你喜欢
  • 1970-01-01
  • 2018-05-28
  • 1970-01-01
  • 2020-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-04
相关资源
最近更新 更多