Навигация по сайту в ASP.NET 2.0 - Создаем карту сайта

ОГЛАВЛЕНИЕ

Создаем карту сайта

Провайдер карты сайта, расширяющий StaticSiteMapProvider (такой, как у нас), должен реализовывать метод BuildSiteMap(), чье свойство служит для создания карты сайта (при необходимости), и затем возвратить корневой SiteMapNode. Поскольку данный метод может быть вызван несколько раз при единственном запросе страницы (поскольку страница может иметь несколько элементов управления навигацией), он предполагает использование кэширования, причем как можно больше. Ведь мы не хотим исследовать и строить карту сайта каждый раз, как вызывается данный метод. Нам скорее  необходимо построить ее один раз и сохранить данное дерево до того момента, когда будет изменена файловая система.

Вы также можете довольно легко сохранить дерево - просто создайте переменную _root SiteMapNode на уровне класса и назначьте данную переменную корню SiteMap. Такой подход позволяет построить карту сайта единожды, и затем сохраняет ее до того, как веб-приложение будет перезагружено либо будет отредактирован класс FileSystemSiteMapProvider. Данное сохранение может показаться слишком "агрессивным", но, тем не менее, в случае  если новые ASP.NET-страницы добавлены в файловую систему либо удаляются существующие, карта сайта не отобразит данные изменения.

Чтобы избавиться от данной проблемы, используйте объект CacheDependency, который может быть использован для слежения за набором файлов и/или каталогов. Чтобы быть более точным, я настраиваю его на корневой каталог (~/), и когда вызывается BuildSiteMap(), я проверяю файловую систему на какие-либо изменения, произошедшие с момента последнего вызова BuildSiteMap(). Если ничего не произошло - я просто возвращаю ссылку _root, так как нет никакой необходимости в перестройке карты сайта. Если же были произведены какие-либо изменения, то я заново собираю карту.

Метод BuildSiteMap() и рекурсивная BuildSiteMapFromFileSystem(), приведенные ниже, являются двигателями создания карты сайта. Я оставил парочку вспомогательных методов (к примеру, CreateFileNode() и CreateFolderNode()). Данные методы вы можете исследовать, скачав полный код в конце данной статьи.

Public Overrides Function BuildSiteMap()  As System.Web.SiteMapNode
    'Необходимо заблокировать для обеспечения безопасности процесса, поскольку может случиться и так, что несколько
    'страниц в приложении могут вызвать данный метод одновременно
    SyncLock Me
        'Проверяем, был ли корень определен
        If _root IsNot Nothing Then
            'У нас есть корень - но была ли файловая система изменена?
            If Not _fsMonitor.HasChanged Then
                'Никаких изменений не произошло - возвращаем сохраненный путь
                Return _root
            End If

            'Файловая система была изменена с момента последнего доступа
            'BuildSiteMap - нам необходимо пересобрать карту сайта
        End If

        'Если мы добрались до этого места, либо у нас нет корня или файловая система была
        'изменена то есть  нам необходимо перестроить карту сайта. Очищаем ее в случае, если она существует
        
        Refresh()

        'Создаем корневой узел
        _root = CreateFolderNode(HttpContext.Current.Server.MapPath(RootUrl), RootUrl)
        _root.Title = RootTitle

        'Устанавливаем зависимость от  сохраненной версии
        _fsMonitor = New CacheDependency(HttpContext.Current.Server.MapPath("~/"))

        AddNode(_root)  'Добавляем путь к карте сайта

        'Рекурсивно проходим по файловой системе, добавляя узлы
        BuildSiteMapFromFileSystem(_root, "~/")

        Return _root
    End SyncLock
End Function

Protected Sub BuildSiteMapFromFileSystem(ByVal parentNode As SiteMapNode, ByVal folderPath As String)
    'Определяем путь к каталогу текущего currentNode
    Dim folder As String = HttpContext.Current.Server.MapPath(folderPath)

    'Получаем информацию о каталоге
    Dim dirInfo As New DirectoryInfo(folder)

    'Добавляем файлы в дерево , которое имеет текущий currentNode в качестве родителя
    For Each fi As FileInfo In dirInfo.GetFiles("*.aspx")
        Dim fileNode As SiteMapNode = CreateFileNode(fi.FullName, parentNode, folderPath)
        If fileNode IsNot Nothing Then AddNode(fileNode, parentNode)
    Next

    'Добавляем узлы для каждой подпапки
    For Each di As DirectoryInfo In dirInfo.GetDirectories()
        Dim folderNode As SiteMapNode = CreateFolderNode(di.FullName, String.Concat(folderPath, di.Name, "/") & DefaultPageName)

        'Добавляем узел
        If folderNode IsNot Nothing Then
            AddNode(folderNode, parentNode)

            BuildSiteMapFromFileSystem(folderNode, String.Concat(folderPath, di.Name, "/"))
        End If
    Next
End Sub