【问题标题】:ASP.NET MVC Recursion ProcedureASP.NET MVC 递归过程
【发布时间】:2015-07-18 15:05:26
【问题描述】:

我正在尝试推断并将一些已回答的有关递归的问题转移到我的一个项目中。

已回答我引用的递归问题:
asp-net-mvc-4-generating-a-treeview-with-recursive-partial-view

但是,我无法理解这个概念并将其应用到我的项目中。

这是我的尝试。

首先,我创建了一个名为 Models 的类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MVCMnetWebsite.Models
{
  public class Category
  {
    public int TAB_ITEM_ID { get; set; }
    public string TAB_ITEM_NAME { get; set; }
    public int? TAB_ITEM_PARENT_ID { get; set; }
    public string URL { get; set; }
    public string WINDOW_NAME { get; set; }
    public string TOOL_TIP { get; set; }
    public string ACCESS { get; set; }
    public DateTime? START_DATE { get; set; }
    public DateTime? END_DATE { get; set; }
    public int? LASTUPDATE_BY { get; set; }
    public int? SNAC_TYPES_OBJID { get; set; }
    public byte? ACTIVE { get; set; }
    public string ICON { get; set; }
}

public class SeededCategories
{
    public int? Seed { get; set; }
    public IList<Category> Categories { get; set; }
}

}

然后,我创建了一个名为 TreeController 的控制器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCMnetWebsite.Models;

namespace MVCMnetWebsite.Controllers
{
  public class TreeController : Controller
  {
    public ActionResult Index()
    {
        var categories = Session["_menu"] as IList<Category>;
        SeededCategories model = new SeededCategories { Seed = null, Categories = categories };
        return View(model);
    }    
  }
}

然后,我创建了一个名为 _TreeCategories 的局部视图:

@model MVCMnetWebsite.Models.SeededCategories
@using MVCMnetWebsite.Models
@{var menuList = Session["_menu"] as List<Category>;}
@{int appID = 72;} @*PKEY OF APP - LOCATED: [INTRAWEB].[dbo][DATA_SN_ACCESS_CONTROL_TYPES]*@

@foreach (var topMenuList in menuList.Where(x => x.TAB_ITEM_PARENT_ID == null && x.SNAC_TYPES_OBJID == appID).Select(x => x).ToList())
{
<li>
    <a href="#"><span><i class="@topMenuList.ICON"></i>  @topMenuList.TAB_ITEM_NAME</span></a>
    <ul>
        @foreach (var subMenuList in menuList.OrderBy(x => x.TAB_ITEM_NAME).Where(x => x.TAB_ITEM_PARENT_ID == topMenuList.TAB_ITEM_ID).Select(x => x).ToList())
        {
            if (menuList.Any(x => x.TAB_ITEM_PARENT_ID == subMenuList.TAB_ITEM_ID))
            {
                <li>
                    <a href="#"><span><i class="fa fa-toggle-right"></i> @subMenuList.TAB_ITEM_NAME</span></a>
                    <ul>
                        @foreach (var subChildMenuList in menuList.Where(x => x.TAB_ITEM_PARENT_ID == subMenuList.TAB_ITEM_ID).Select(x => x).ToList())
                        {
                            <li class="urlLink">
                                <a name="@subChildMenuList.URL" href="">@subChildMenuList.TAB_ITEM_NAME</a>
                            </li>
                        }
                    </ul>
                </li>
            }
            else
            {
                <li class="urlLink"><a name="@subMenuList.URL" href="">@subMenuList.TAB_ITEM_NAME </a></li>
            }
        }
    </ul>
</li>
}

在我的 _layout.cshtml 中,我有:

@Html.Partial("_TreeCategories", Model)

从上面显示的代码中,我只能显示直到第二个孩子的项目。如何使用递归来显示最多第 N 个孩子的项目?

已编辑:

@if (Model.Categories.Where(s => s.TAB_ITEM_PARENT_ID == Model.Seed).Any())
{
  <ul>
    @foreach (var node in Model.Categories)
    {
        if (node.TAB_ITEM_PARENT_ID == Model.Seed)
        {
            SeededCategories inner = new SeededCategories { Seed = node.TAB_ITEM_ID, Categories = Model.Categories };
            <li>
                <a href="?@node.TAB_ITEM_ID">@node.TAB_ITEM_NAME</a>
                @Html.Partial("_TreeCategories", inner)
            </li>
        }
    }
</ul>
}

上面的代码可以工作,但需要调整才能呈现正确的样式。此外,上面的代码取自顶部提到的链接。我只是不知道在哪里添加我的 HTML 结构 - 意思是:上面的代码以无序列表开头,而我的以列表项开头 HTML 不同,我看不到模式以及如何将其转移到我的情况。

【问题讨论】:

  • 我没有看到您在模型中创建树的位置?
  • 递归方法是一种调用自身的方法(在你的代码中没有你这样做的地方)并且在任何情况下你的模型都没有层次结构 - 你应该从视图模型开始(比如说) CategoryVM 包含一个属性 List&lt;CategoryVM&gt; ChildCategories
  • @StephenMuecke,正确 - 我没有调用任何递归方法,这就是我寻求帮助的原因。我在哪里/如何实现递归?我要看看我今天是否可以使用 Erik Philips 的答案来解决这个问题。谢谢。

标签: c# asp.net asp.net-mvc linq recursion


【解决方案1】:

我设法解决了我自己的问题。所以,@ErikPhilips 指出我没有创建树模型。经过一番摆弄,我解决了我的问题。希望这可以帮助任何有类似情况的人。

@if (Model.CATEGORIES.Where(s => s.TAB_ITEM_PARENT_ID ==     Model.TAB_ITEM_CHILD_ID).Any())
{
foreach (var node in Model.CATEGORIES)
{
    if (node.TAB_ITEM_PARENT_ID == Model.TAB_ITEM_CHILD_ID)
    {
        SeededCategories inner = new SeededCategories { TAB_ITEM_CHILD_ID = node.TAB_ITEM_ID, CATEGORIES = Model.CATEGORIES };

        bool hasChildren = Model.CATEGORIES.Any(x => x.TAB_ITEM_PARENT_ID == node.TAB_ITEM_ID);
        bool hasParents = Model.CATEGORIES.Any(x => x.TAB_ITEM_ID == node.TAB_ITEM_PARENT_ID);

        <li>
            @if (hasChildren && !hasParents)
            {
                <a href="#"><span><i class="@node.ICON"></i> @node.TAB_ITEM_NAME</span></a>
            }
            else if (hasChildren && hasParents)
            {
                <a href="@node.URL"><span><i class="fa fa-toggle-right"></i> @node.TAB_ITEM_NAME</span></a>
            }
            else
            {
                <a href="@node.URL"><span><i class="#"></i> @node.TAB_ITEM_NAME</span></a>
            }
            @if (hasChildren)
            {
                <ul>
                    @Html.Partial("_TreeCategories", inner)
                </ul>
            }
        </li>
    }
  }
}

【讨论】:

    【解决方案2】:

    我个人不喜欢使用简单的局部递归,因为堆栈会增长,您可以获得Stack Overflow

    我将向您展示一种从非树结构创建树结构的简单方法,以及如何在没有递归的情况下在视图中创建树结构(听起来 CraAAzzzyYY!?)。

    在某些时候你应该有一个看起来像这样的对象:

    public class NodeVM
    {
      public int Id { get; set; }
      public int PartentId { get; set; }
      public string Name { get; set; }
    
      public IEnumerable<NodeVM> Nodes { get; set; }
      public MvcHtmlString EndTag { get; set; }
    {
    
    public class TreeVM
    {
      public Stack<NodeVM> Nodes { get; set; }
    }
    

    如果您从一个节点列表开始而没有填充 Nodes 属性,这是填充节点的简单方法:

    // get our nodes from the database:
    var nodes = db.Nodes
      .Select(n => new NodeVM()
      {
        // map fields or use AutoMapper... whatever
      })
      .ToList(); 
    
    nodes = nodes.Select(node =>
      {
        node.Nodes = nodes.Where(subnode => subnode.ParentID == node.Id).Tolist();
        return node;
      })
      .ToList();
    
      var model = new TreeVM();
      model.Nodes = new Stack(nodes
        // Find only Parent Elements
        .Where(node => !nodes.Any(subnode => subnode.ParentiD = node.Id));
        // Reverse list, we are using a stack (LILO)
        .Reverse()
        .ToList());
    

    现在假设我们想要构建树的 html,如下所示:

    <ul>
      <li> A
        <ul>
          <li> A 1 </li>
          <li> A 2 </li>
        </ul>
      </li>
      <li> B
        <ul>
          <li> B 1 </li>
          <li> B 2 </li>
        </ul>
      </li>
    <ul>
    

    这是相当直接的 HTML...,现在进入剃须刀:

    @model TreeVM
    
    <ul>
      @while(Model.Nodes.Count > 0)
      {
        var currentNode = Model.Nodes.Pop();
    
        @* we reached a node with an EndTag Value
           must render closing tag 
        *@
        if (!currentNode.EndTag.IsNullOrEmpty())
        {
          @: @currentNode.EndTag 
        }
        @* we reached a node we haven't seen before
        *@
        else
        {
          // Create our node <LI>
          var li = new TagBuilder("li");
          li.AddCssClass("my css classes");
          li.MergeAttributes("id", currentNode.Id.ToString();
    
          var endTag = li.ToString(TagRenderMode.EndTag);
    
          // Render out our LI start tag
          @: @(new MvcHtmlString(li.ToString(TagRenderMode.StartTag)))
    
          // Do we have subnodes?
          var hasSubNodes = currentNode.Nodes.Count > 0;
    
          if (hasSubNodes)
          {
            // Need a new UL
            var ul = new TagBuilder("ul");
            li.AddCssClass("my css classes");
            li.MergeAttributes("id", currentNode.Id.ToString();
    
            // Render out our UL start Tag
            @: @(new MvcHtmlString(ul.ToString(TagRenderMode.StartTag)))
    
            // End tag should be opposite order </ul></li>
            endTag = ul.ToString(TagRenderMode.EngTag) + endTag;
          }
    
          // Put the node back in the stack
          currentNode.EndTag = new MvcHtmlString(endTag);
          Model.Nodes.Push(currentNode);
    
          // Push all sub nodes in the stack
          if (hasSubNodes)
          {
            foreach(var subNode in currentNode.Nodes)
            {
              Model.Nodes.Push(subNode);
            }
          }
    
          <text>
          Whatever you want in the LI
          </text>
    
        }
      }
    

    【讨论】:

    • 感谢 Erik 的回答,你能解释一下为什么像这样手动使用堆栈不会导致堆栈溢出异常吗?我的意思是我们将有子节点的节点推回堆栈顶部,这里是什么阻止堆栈增长到堆栈溢出点?
    • 我没有使用堆栈,我的代码从不调用自身,也从不调用其他嵌套调用。根据您的评论,我认为您不了解 StackOverflowException 是什么。
    • 你不是在 TreeVM 模型中使用 Stack 吗?然后Model.Nodes.Push(subNode); 也许我混淆了堆栈数据结构(类)的使用与溢出的调用堆栈?我从 Phil Haack 的博客中阅读了这篇文章:haacked.com/archive/2007/03/04/…,我认为这是因为您没有递归调用任何东西,也没有像普通递归函数那样使用调用堆栈,或者如果不是这样,我猜这有点超出我的理解范围。
    • 啊.. 不要将 Stack 类与 The Stack 混淆。
    猜你喜欢
    • 1970-01-01
    • 2015-04-03
    • 1970-01-01
    • 2011-09-19
    • 2010-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多