从分层sql数据返回无序列表

时间:2009-03-12 00:31:39

标签: asp.net html sql-server-2005

我有tableId,parentPageId,title columns的表。

有没有办法使用asp.net,cte,存储过程,UDF返回无序的嵌套列表......什么?

表格如下:

PageID    ParentId    Title
1         null        Home
2         null        Products
3         null        Services
4         2           Category 1
5         2           Category 2
6         5           Subcategory 1
7         5           SubCategory 2
8         6           Third Level Category 1
...  

结果应如下所示:

Home
Products
    Category 1
        SubCategory 1
            Third Level Category 1
        SubCategory 2
    Category 2
Services

理想情况下,列表也应包含<a>标记,但如果我找到创建<ul>列表的方法,我希望自己可以添加它。

编辑1:我认为已经有了解决方案,但似乎没有。我希望尽可能保持简单并且不惜任何代价逃避使用ASP.NET菜单,因为它默认使用表。然后我必须使用CSS Adapters等。

即使我决定沿着“ASP.NET菜单”路线走,我也只能找到这种方法:http://aspalliance.com/822使用DataAdapter和DataSet :(

更现代或更有效的方式?

5 个答案:

答案 0 :(得分:5)

使用linq2sql你可以这样做:

List<PageInfo> GetHierarchicalPages()
{
   var pages = myContext.PageInfos.ToList();
   var parentPages = pages.Where(p=>p.ParentId == null).ToList();
   foreach(var page in parentPages)
   {
      BuildTree(
        page, 
        p=> p.Pages = pages.Where(child=>p.pageId == child.ParentId).ToList()
        );
   }
}
void BuildTree<T>(T parent, Func<T,List<T>> setAndGetChildrenFunc)
{
   foreach(var child in setAndGetChildrenFunc(parent))
   {
       BuildTree(child, setAndGetChildrenFunc);
   }
}

假设您在PageInfo中定义Pages属性,如:

public partial class PageInfo{
   public List<PageInfo> Pages{get;set;}
}

在Web应用程序端进行在层次结构上获取它的处理,这避免了sql server上的额外负载。另请注意,此类信息是缓存的理想选择。

您可以像雷克斯提到的那样进行渲染。或者,您可以在此实现上进行一些扩展,并使其支持层次结构接口并使用asp.net控件。

更新1:对于您在评论中询问的渲染变体,您可以:

var sb = new System.IO.StringWriter();
var writer = new HtmlTextWriter(sb);
// rex's rendering code
var html = sb.ToString();

答案 1 :(得分:2)

最佳做法是使用IHierarchyDataIHierarchalEnumerable以及DataBind执行此操作,以继承自HierarchalDataBoundControl的自定义控件(这是TreeView等控件的基础)

但是,让我们尝试在c#中使用快速而肮脏,不是特别有效的简单示例:

//class to hold our object graph in memory
//this is only a good idea if you have a small number of items
//(less than a few thousand)
//if so, this is a very flexible and reusable way to represent your tree
public class Page
{
    public string Title {get;set;}
    public int ID {get;set;}
    public Collection<Page> Pages = new Collection<Page>();

    public Page FindPage(int id)
    {
        return FindPage(this, id);
    }

    private Page FindPage(Page page, int id)
    {
        if(page.ID == id)
        {
            return page;
        }
        Page returnPage = null;
        foreach(Page child in page.Pages)
        {
            returnPage = child.FindPage(id);
            if(returnPage != null)
            {
                break;
            }
        }
        return returnPage;
    }
}

//construct our object graph
DataTable data = SelectAllDataFromTable_OrderedByParentIDAscending();
List<Page> topPages = new List<Page>();
foreach(DataRow row in data.Rows)
{
    Page page = new Page();
    page.Title = (string)row["Title"];
    page.ID = (int)row["PageID"];
    if(row["ParentID"] == null)
    {
        topPages.Add(page);
    }
    else
    {
        int parentID = (int)row["ParentID"];
        foreach(Page topPage in topPages)
        {
            Page parentPage = topPage.FindPage(parentID);
            if(parentPage != null)
            {
                parentPage.Pages.Add(page);
                break;
            }
        }
    }
}

//render to page
public override void Render(HtmlTextWriter writer)
{
    writer.WriteFullBeginTag("ul");
    foreach(Page child in topPages)
    {
        RenderPage(writer, child);
    }
    writer.WriteEndTag("ul");
}

private void RenderPage(HtmlTextWriter writer, Page page)
{
    writer.WriteFullBeginTag("li");
    writer.WriteBeginTag("a");
    writer.WriteAttribute("href", "url");
    writer.Write(HtmlTextWriter.TagRightChar);
    writer.Write(page.Title);
    writer.WriteEndTag("a");
    if(page.Pages.Count > 0)
    {
        writer.WriteFullBeginTag("ul");
        foreach(Page child in page.Pages)
        {
            RenderPage(writer, child);
        }
        writer.WriteEndTag("ul");
    }
    writer.WriteEndTag("li");
}

答案 2 :(得分:0)

这应该让你开始。

with x (pageID, title)
      as (
  select cast(title as varchar(100)),pageID
    from pages
   where parentID is null
   union all
  select cast(x.title||' - '||e.title as varchar(100)),
         e.pageID
    from pages e, x
   where e.parentID = x.pageID
  )
  select title as title_tree
    from x
   order by 1

输出:

TITLE_TREE
Home
Products
Services
Products - Category 1 
Products - Category 2
Products - Category 2 - Subcategory 1 
Products - Category 2 - Subcategory 1 - Third Level Category 1
Products - Category 2 - Subcategory 2

答案 3 :(得分:0)

您是否考虑过使用SELECT从SQL Server获取XML输出... FOR XML EXPLICIT?您的数据似乎完全符合要求。

举个例子:

http://www.eggheadcafe.com/articles/20030804.asp

如果你想追求,我可以通过一个例子。

答案 4 :(得分:0)

RexM - 首先我必须声明我是一名前端开发人员,因此甚至无法触及您的编码C#的技能和知识。但是 - 我确实使用Page对象实现了您的解决方案并遇到了问题。是的,对不起,在这种情况下,我是一个“请发送给我的代码”水蛭,但是从不遗憾,认为详细说明“虫子”非常重要。

我正在构建一个使用嵌套UL显示菜单项的网站,并允许用户根据需要重新排序菜单。

我的菜单包含以下数据字段:pageID,parentID,pageOrder,pageTitle

页面顺序是指页面在节点中的显示顺序。

所以我对SelectAllDataFromTable_OrderedByParentIDAscending();的查询是:

SELECT * FROM [pages] ORDER BY [parentID] ASC, [pageOrder] ASC

然后我使用jsTree使菜单项可拖动和可拖放。

我重新订购了几页并发现了一个错误:

说我的结构是这样的:

home
  cars
    usa
      muscle cars
      suvs
    europe
  colours
  directions
    vertical
    horizontal
      up
      down

如果我将“汽车”(及其所有孩子)移到“向下”里面,“汽车”的孩子就不再显示在菜单中了。那就是“虫子”。

我已经检查了数据库并且 parentID pageOrder 在“cars”下都是正确的,我也尝试更改我的SQL查询,从头开始,进行各种测试直接在DB上(以上所有关闭jsTree所以我可以看到基本的嵌套UL) - 但没有成功。

只是想知道,正如我看到其他论坛指向此页面的解决方案将分层sql数据转换为嵌套的UL,可能值得有人研究它。

由于我的整个网站都基于Javascript的使用,我现在已经实现了一个Jquery.ajax解决方案(非常糟糕的评论,在my site这里)来构建嵌套的UL,但正如我所说,只是标记为潜在的问题。

非常感谢我自己找到解决方案的一脚开始!