Вернуть неупорядоченный список из иерархических данных SQL - PullRequest
2 голосов
/ 12 марта 2009

У меня есть таблица с pageId, parentPageId, столбцами заголовков.

Есть ли способ вернуть неупорядоченный вложенный список, используя 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 и т. Д.

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

Есть ли более современный или эффективный способ?

Ответы [ 5 ]

5 голосов
/ 12 марта 2009

Используя 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();
2 голосов
/ 12 марта 2009

Рекомендуется делать это, используя IHierarchyData и IHierarchalEnumerable и 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");
}
0 голосов
/ 08 апреля 2010

RexM - во-первых, я должен заявить, что я являюсь разработчиком внешнего интерфейса, поэтому не могу даже потрогать вас за навыки и знания кодирования на 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

Если я перемещу «автомобили» (и все это дети) внутри «вниз», дети «автомобилей» больше не отображаются в меню. Это «ошибка».

Я проверил, правильно ли введены db и parentID и pageOrder в разделе "cars", я также попытался изменить свой SQL-запрос, начиная с нуля, все виды тестирования непосредственно на DB (все вышеперечисленное с отключенным jsTree, чтобы я мог видеть основной вложенный UL) - но безуспешно.

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

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

Большое спасибо за то, что я нашел способ найти решение!

0 голосов
/ 12 марта 2009

Рассматривали ли вы получение вывода XML с SQL Server с помощью SELECT ... FOR XML EXPLICIT? Ваши данные, кажется, настроены идеально для этого.

Для примера:

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

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

0 голосов
/ 12 марта 2009

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

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...