Возвращает неупорядоченный список из иерархических данных sql


У меня есть таблица со столбцами pageId, parentPageId, title.

Есть ли способ, чтобы вернуть вложенные неупорядоченные списки, используя asp.net, КТР, хранимые процедуры, ОДС... хоть что-нибудь?

Таблица выглядит так:

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>.

EDIT 1: я думал, что уже есть решение для этого, но похоже, что я хотел сохранить его простым, насколько это возможно, и избежать использования ASP.NET меню любой ценой, потому что оно использует таблицы по умолчанию. Затем я должен использовать CSS-адаптеры и т. д.

Даже если я решу спуститься по "ASP.NET меню" маршрут мне удалось найти только при таком подходе: http://aspalliance.com/822 который использует DataAdapter и DataSet : (

Какой-нибудь более современный или эффективный способ?

5 2

5 ответов:

Лучшие практики будет делать это с помощью IHierarchyData и IHierarchalEnumerable и привязки к пользовательский элемент управления, который наследует от 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");
}

Используя 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);
   }
}

Предполагая, что вы определяете свойство Pages в PageInfo следующим образом:

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

Обработка, чтобы получить его в иерархии происходит на стороне веб-приложения, что позволяет избежать дополнительной нагрузки на sql-сервер. Также обратите внимание, что этот тип информации является идеальным кандидатом для кэширования.

Вы можете сделать рендеринг, как упоминал Рекс. В качестве альтернативы вы можете немного расширить эту реализацию и сделать ее поддерживающей интерфейсы иерархии и использование asp.net контроли.

Обновление 1: для варианта рендеринга, который вы спросили в комментарии, Вы можете:

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

Это должно помочь вам начать.

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

Рассматривали ли вы возможность получения XML-вывода из SQL Server с помощью SELECT ... ДЛЯ XML ЯВНОГО? Ваши данные, кажется, созданы идеально для этого.

Для примера:

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

Если вы хотите продолжить, я мог бы работать через пример.

RexM -Во-первых, я должен заявить, что я front-end разработчик, поэтому не могу даже прикоснуться к вам за мастерство и знание кодирования C#. Однако-я реализовал ваше решение с помощью объекта Page и столкнулся с проблемой. Да, извините, что я пиявка" pleaseSendMeTheCode "в этом случае, но никогда не думал, что важно подробно описать"ошибку".

Я создаю сайт, который использует вложенный 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 - запрос, начиная с нуля, все виды тестирования непосредственно на БД (все вышеперечисленное с jstree отключено, чтобы я мог видеть основной вложенный UL) - но без успеха.

Просто интересно, как я видел другие форумы, указывающие на эту страницу для решений по превращению иерархических данных sql во вложенные UL, это может быть стоит кого-то смотрю в него.

Поскольку весь мой сайт основан на использовании Javascript, я теперь реализовал Jquery.ajax решение (которое, очень плохо прокомментировано, находится на моем сайте здесь) для построения вложенного UL, но, как я уже сказал, просто помечает как потенциальную проблему.

Спасибо большое, хотя за толчок в моем собственном поиске решения!