2010-12-08 9 views
1

Ich arbeite an einem DotNetNuke-Modul, das ein Navigationsmenü im Baumstil enthält.Rekursives SQL zum Erstellen eines DNN-Navigationsmenüs

Bis jetzt habe ich es funktioniert, in dem Sinne, dass Kind-Knoten mit ihren richtigen Eltern-Knoten verbunden sind, aber die Knoten-Geschwister sind immer noch außer Betrieb. Es gibt ein Feld namens TabOrder, mit dem die Reihenfolge der Geschwister bestimmt wird, aber aufgrund der Rekursion kann ich sie nicht richtig sortieren.

Ich versuche dies in einer gespeicherten SQL Server-Prozedur zu tun, was ein Fehler sein kann, aber ich fühle mich so nah, dass es eine Lösung geben muss. Hat jemand eine Idee, was ich falsch mache?

Ich würde mich über alle Ideen freuen, die Sie haben. Danke im Voraus.


Lösung:

fand ich endlich eine Lösung für meine Frage. Der Schlüssel war, eine Tab-Lineage (TabLevel + TabOrder) rekursiv von der Root-Registerkarte zu den Leaf-Tabs zu erstellen. Sobald das erstellt wurde, konnte ich die zurückgegebenen Datensätze ordnungsgemäß bestellen.

Als ich jedoch zurückkam, um dies zu posten, sah ich MarkXA die Antwort, die wahrscheinlich die beste Lösung ist. Ich wusste nicht, dass die Methode GetNavigationNodes überhaupt existiert.

Ich denke, er ist richtig, dass die Verwendung von GetNavigationNodes ist eine zukunftssichere Lösung, aber für den Moment werde ich meine SQL-basierte Lösung verwenden. --Was kann ich sagen? Ich lerne auf die harte Tour.

Hier ist sie:

ALTER procedure [dbo].[Nav_GetTabs] 
    @CurrentTabID int = 0 
AS 

--============================================================ 
--create and populate @TabLineage table variable with Tab Lineage 
-- 
--"Lineage" consists of the concatenation of TabLevel & TabOrder, concatenated recursively from the root to leaf. 
--The lineage is VERY important, making it possible to properly order the Tab links in the navigation module. 
--This will be used as a lookup table to match Tabs with their lineage. 
--============================================================ 
DECLARE @TabLineage table 
    (
     TabID  int, 
     Lineage  varchar(100) 
    ); 

WITH TabLineage AS 
(
    --start with root Tabs 
    SELECT T.TabID, T.ParentID, CAST(REPLICATE('0', 5 - LEN(CAST(T2.[Level] as varchar(10)) + CAST(T2.TabOrder as varchar(10)))) + CAST(T2.[Level] as varchar(10)) + CAST(T2.TabOrder as varchar(10)) as varchar(100)) AS Lineage 
     FROM Tabs T 
      INNER JOIN Tabs T2 ON T.TabID = T2.TabID 
      INNER JOIN TabPermission TP ON T.TabID = TP.TabID 
     WHERE T.ParentID IS NULL 
      AND T.IsDeleted = 0 
      AND T.IsVisible = 1 
      AND TP.RoleID = -1 

    UNION ALL 

    --continue recursively, from parent to child Tabs 
    SELECT T.TabID, T.ParentID, CAST(TL.Lineage + REPLICATE('0', 5 - LEN(CAST(T2.[Level] as varchar(10)) + CAST(T2.TabOrder as varchar(10)))) + CAST(T2.[Level] as varchar(10)) + CAST(T2.TabOrder as varchar(10)) as varchar(100)) AS Lineage 
     FROM Tabs T 
      INNER JOIN Tabs T2 ON T.TabID = T2.TabID 
      INNER JOIN TabPermission TP ON T.TabID = TP.TabID 
      INNER JOIN TabLineage TL ON T.ParentID = TL.TabID 
     WHERE T.IsDeleted = 0 
      AND T.IsVisible = 1 
      AND TP.RoleID = -1 
) 
--insert results of recursive query into temporary table 
INSERT @TabLineage 
    SELECT TL.TabID, TL.Lineage FROM TabLineage TL ORDER BY TL.Lineage 
    OPTION (maxrecursion 10); --to increase number of traversed generations, increase "maxrecursion" 


--============================================================ 
--create and populate @Ancestor table variable with @CurrentTab ancestors 
-- 
--"Ancestors" are Tabs following the path from @CurrentTab to the root Tab it's descended from (inclusively). 
--These are Tab links we want to see in the navigation. 
--============================================================ 
DECLARE @Ancestor table 
    (
     TabID  int 
    ); 

WITH Ancestor AS 
(
    --start with @CurrentTab 
    SELECT T.TabID, T.ParentID FROM Tabs T WHERE T.TabID = @CurrentTabID 

    UNION ALL 

    --continue recursively, from child to parent Tab 
    SELECT T.TabID, T.ParentID 
     FROM Ancestor A INNER JOIN Tabs T ON T.TabID = A.ParentID 
) 

--insert results of recursive query into temporary table 
INSERT @Ancestor 
    SELECT A.TabID FROM Ancestor A 
    OPTION (maxrecursion 10); --to increase number of traversed generations, increase "maxrecursion" 


--============================================================ 
--retrieve Tabs to display in navigation 

--This section UNIONs three query results together, giving us what we want: 
-- 1. All Tabs at Level 0. 
-- 2. All Tabs in @CurrentTab's lineage. 
-- 3. All Tabs which are children of Tabs in @CurrentTab's lineage. 
--============================================================ 
WITH TabNav (TabID, TabLevel, TabName, Lineage) AS 
(
    --retrieve all Tabs at Level 0 -- (Root Tabs) 
    (SELECT T.TabID, T.[Level] AS TabLevel, T.TabName, TL.Lineage 
    FROM Tabs T 
     INNER JOIN TabPermission TP ON (T.TabID = TP.TabID AND TP.RoleID = -1) 
     INNER JOIN @TabLineage TL ON T.TabID = TL.TabID 
    WHERE T.IsDeleted = 0 
     AND T.IsVisible = 1 
     AND T.[Level] = 0 

    UNION 

    --retrieve Tabs in @CurrentTab's lineage 
    SELECT T.TabID, T.[Level] AS TabLevel, T.TabName, TL.Lineage 
    FROM Tabs T 
     INNER JOIN TabPermission TP ON (T.TabID = TP.TabID AND TP.RoleID = -1) 
     INNER JOIN @Ancestor A ON T.TabID = A.TabID 
     INNER JOIN @TabLineage TL ON T.TabID = TL.TabID 
    WHERE T.IsDeleted = 0 
     AND T.IsVisible = 1 

    UNION 

    --retrieve Tabs which are children of Tabs in @CurrentTab's lineage 
    SELECT T.TabID, T.[Level] AS TabLevel, T.TabName, TL.Lineage 
    FROM Tabs T 
     INNER JOIN TabPermission TP ON (T.TabID = TP.TabID AND TP.RoleID = -1) 
     INNER JOIN @Ancestor A ON T.ParentID = A.TabID 
     INNER JOIN @TabLineage TL ON T.TabID = TL.TabID 
    WHERE T.IsDeleted = 0 
     AND T.IsVisible = 1) 
) 

--finally, return the Tabs to be included in the navigation module 
SELECT TabID, TabLevel, TabName FROM TabNav ORDER BY Lineage; 
--============================================================ 
+0

Die Abfrage war hilfreich, dass ich ein Bootstrap-Menü mock machen musste. Das SQL war gut genug, um mir zu helfen, Listen-Tags zu erzeugen. – Greg

Antwort

2

Die Antwort ist "nicht SQL verwenden". Es gibt bereits eine Methode DotNetNuke.UI.Navigation.GetNavigationNodes, die dies für Sie erledigt, und wenn Sie es verwenden, wird Ihr Modul nicht brechen, wenn sich das Datenbankschema ändert. Selbst wenn Sie etwas tun müssen, das GetNavigationNodes nicht verarbeiten kann, ist es immer noch besser, die Seiten über die API abzurufen, um zukunftssicher zu sein. Direkt in die Datenbank zu gehen fragt nur nach Ärger :)

+0

doh ... jetzt sagt er mir nicht. ;) Ich habe gerade meine SQL-basierte Lösung funktioniert. Ich werde in die GetNavigationNodes-Methode schauen, ich wusste nicht einmal, dass es existiert. Danke, dass du mich darüber informiert hast. – ks78

1

hier ist ein Kesselblech (nicht auf der Grundlage der gegebenen OP Code) Beispiel eines rekursiven Baum CTE, die zeigt, wie ein Baum sortieren:

DECLARE @Contacts table (id varchar(6), first_name varchar(10), reports_to_id varchar(6)) 
INSERT @Contacts VALUES ('1','Jerome', NULL) -- tree is as follows: 
INSERT @Contacts VALUES ('2','Joe' ,'1')  --      1-Jerome 
INSERT @Contacts VALUES ('3','Paul' ,'2')  --     /  \ 
INSERT @Contacts VALUES ('4','Jack' ,'3')  --    2-Joe   9-Bill 
INSERT @Contacts VALUES ('5','Daniel','3')  --   /  \    \ 
INSERT @Contacts VALUES ('6','David' ,'2')  --  3-Paul   6-David  10-Sam 
INSERT @Contacts VALUES ('7','Ian' ,'6')  -- / \   / \ 
INSERT @Contacts VALUES ('8','Helen' ,'6')  -- 4-Jack 5-Daniel 7-Ian 8-Helen 
INSERT @Contacts VALUES ('9','Bill ' ,'1')  -- 
INSERT @Contacts VALUES ('10','Sam' ,'9')  -- 

DECLARE @Root_id varchar(6) 

--get all nodes 2 and below 
SET @Root_id=2 
PRINT '@Root_id='+COALESCE(''''[email protected]_id+'''','null') 
;WITH StaffTree AS 
(
    SELECT 
     c.id, c.first_name, c.reports_to_id, c.reports_to_id as Manager_id, cc.first_name AS Manager_first_name, 1 AS LevelOf 
     FROM @Contacts     c 
      LEFT OUTER JOIN @Contacts cc ON c.reports_to_id=cc.id 
     WHERE [email protected]_id OR (@Root_id IS NULL AND c.reports_to_id IS NULL) 
    UNION ALL 
     SELECT 
      s.id, s.first_name, s.reports_to_id, t.id, t.first_name, t.LevelOf+1 
     FROM StaffTree   t 
      INNER JOIN @Contacts s ON t.id=s.reports_to_id 
    WHERE [email protected]_id OR @Root_id IS NULL OR t.LevelOf>1 
) 
SELECT * FROM StaffTree ORDER BY LevelOf, first_name 

OUTPUT:

@Root_id='2' 
id  first_name reports_to_id Manager_id Manager_first_name LevelOf 
------ ---------- ------------- ---------- ------------------ ----------- 
2  Joe  1    1   Jerome    1 
6  David  2    2   Joe    2 
3  Paul  2    2   Joe    2 
5  Daniel  3    3   Paul    3 
8  Helen  6    6   David    3 
7  Ian  6    6   David    3 
4  Jack  3    3   Paul    3 

(7 row(s) affected) 

der Schlüssel ist die LevelOf Spalte. Sehen Sie, wie es nur ein Literal ist 1 bei der Auswahl der Hauptelternteil in der CTE. Die LevelOf-Spalte wird dann in dem UNION ALL-Abschnitt des rekursiven CTE inkrementiert. Jeder rekursive Aufruf (nicht Zeile) an den CTE wird das UNION ALL einmal und das Inkrement treffen. Nicht viel mehr als das.

+0

Ihr Beispiel ist so ähnlich wie das, was ich mache. Ich muss es mehr studieren, um zu sehen, ob es die Lösung enthält. Danke für die Antwort. – ks78

+0

Verwenden Sie DotNetNuke? Wenn ja, können Sie meine gespeicherte Prozedur ausprobieren und die Tab-ID einer 3-4 Level-Registerkarte übergeben, um mein Problem zu sehen. – ks78

+0

Entschuldigung, aber ich benutze DotNetNuke –