Thomas Bandt

Über mich | Kontakt | Archiv

[ASP.NET 2.0] SiteMaps on the fly aus der Datenbank erstellen

Ein Thema, was mir nach ersten halbwegs erfolgreichen Versuchen Anfang des Jahres ständig unter den Nägeln brannte, schob sich am Donnerstag in meiner ToDo-Liste wieder nach oben, weshalb ich es dann auch endlich mal richtig in Angriff genommen habe: das Erstellen eines Custom-SiteMap-Providers, der die Daten für eine SiteMap on the fly aus der Datenbank holt.

Damit kann man dann die vielfältigen fertigen Controls, die ASP.NET 2.0 mit bringt, auch in seiner eigenen dynamischen Anwendung für Einsatzzwecke verwenden, die die Standardfunktionalität der Controls bei weitem übersteigen. In meinem Fall geht es zum Beispiel darum eine Menüstruktur, die vollständig in der Datenbank über eine einfache Child-Parent-Beziehung gehalten wird, für die Menü- und SiteMapPath-Controls zugängig zu machen. Damit kann ich dann im Produktionsprozess innerhalb weniger Minuten anspruchsvolle Menüs und Navigationslösungen in unser CMS-Frontend implementieren.

Die Anforderungen, die sich daraus ergeben sehen wie folgt aus:

Warum das Ganze so einen Aufwand bedeutet hat? Ganz einfach - weil ich keine Lösung zum Thema im Netz finden konnte. Auch der Artikel von Jeff Prosise war nur als Einstieg in die Thematik zu gebrauchen, da hieß es wohl zu früh gefreut. Der Grund: auch er setzt, wie die meisten anderen Beispielimplementierungen, auf den StaticSiteMapProvider.

Der hat aber ein paar ganz gravierende Nachteile:

  1. Eine URL fungiert quasi auch als Key. Sie muss eindeutig sein. Es ist nicht möglich den gleichen Link mehrfach vorzuhalten. Dafür gibt es zwar Workarounds - aber wozu schöne URLs bauen, um sie sich dann wieder von irgendwelchen angehängten QueryStrings kaputt machen zu lassen. Im Hintergrund macht das Verhalten übrigens durchaus Sinn - wenn es dann darum geht mit "securityTrimming=true" zu arbeiten ... aber das war hier nicht gegeben und Bestandteil der Aufgabe.
  2. Es funktionieren keine externen URLs.

So, was nun? Die Lösung lag in der Verwendung der Basisklasse SiteMapProvider, anstatt StaticSiteMapProvider. Diese erscheint zwar auf den ersten Blick schwieriger zu implementieren, auf den Zweiten hielt sich der Aufwand dann aber doch in Grenzen.

Anbei habe ich ein Beispielprojekt gepackt, was aus einer SQLExpress-Datenbank ein Menü generiert:

Das Ganze wird noch in den Cache gelegt (allerdings über eine Textfile-Dependency invalidiert) und es ist auch möglich mehrere Versionen der SiteMap anzulegen. Außerdem ist es bereits (in einer etwas komplexeren Form) erfolgreich in einer Multi-User-Umgebung getestet worden, d.h. wenn sich User 1 einloggt bekommt User 2 nicht plötzlich auch dessen Menü mit womöglich geheimen Punkten angezeigt, so viel ist sicher. ;-)

Ich werde hier nicht näher auf den Code eingehen, da ich davon ausgehe, dass jeder, den das Thema interessiert, und der so etwas selbst implementieren will, es schaffen wird sich in den Code einzulesen. Wer Fragen hat, kann mir die aber natürlich trotzdem gerne stellen - die Kontaktdaten stehen auf der linken Seite (im Blog, lieber RSS-Leser ;-)).

Hier das Beispielprojekt, viel Spaß:

SqlSiteMapProvider.rar (156,73 KB)

Kommentare

  1. Sascha schrieb am Mittwoch, 29. März 2006 22:18:00 Uhr:

    Hi Leute,

    hat jemand mal das Treeview-Control mit Thomas' Provider getestet. Bei mir zeigt der nur den Root-Node an. Ist der Datentyp uniqueidentifier hier von besonderem Nutzen oder ist der bei Trees nicht eher hinderlich? Der Provider ist aber super. Er hat mir ne Menge Zeit gespart.

    Grüße
    Sascha
  2. Thomas schrieb am Mittwoch, 29. März 2006 22:38:00 Uhr:

    Hi,

    versuch mal im DataSource einzustellen, dass der Rootnode übersprungen wird (genauer Name des Attr. fällt mir grad nicht ein).
  3. Sascha schrieb am Mittwoch, 10. Mai 2006 22:52:00 Uhr:

    Hi Thomas,

    habe seit längerem noch ein wenig mit Deiner SQLSiteMapProvider-Version herumgespielt. Mit ist aufgefallen, dass:
    protected virtual SiteMapNode BuildSiteMap()

    die nodeCollection nicht richtig aufbaut.

    if (categories[i + 1] != null && categories[i + 1].Level > categories[i].Level)
    parentNode = childNode;

    macht nicht zwingend den richtigen node zum parent. Sollten in Deinem Beispiel zwischen der Anlage des Parents und des Childs andere Datensätze in die Tabelle gekommen sein, so werden die Childs an den falschen Parent gehängt. Hier tut wohl auch Rekursion im Code not... Ich schau mal ob ich es gelöst bekomme. Liebe Grüße von Sascha


« Zurück  |  Weiter »