Возвращает неупорядоченный список из иерархических данных 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 ответов:
Лучшие практики будет делать это с помощью 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, но, как я уже сказал, просто помечает как потенциальную проблему.
Спасибо большое, хотя за толчок в моем собственном поиске решения!