Создание пользовательского поставщика карт сайтов, управляемых базами данных (VB)

Скотт Митчелл

Загрузить PDF-файл

Поставщик карты сайта по умолчанию в ASP.NET 2.0 извлекает свои данные из статического XML-файла. Хотя поставщик на основе XML подходит для многих небольших и средних веб-сайтов, более крупным веб-приложениям требуется более динамичная карта сайта. В этом руководстве мы создадим настраиваемый поставщик карты сайта, который получает свои данные из уровня бизнес-логики, который, в свою очередь, получает данные из базы данных.

Введение

функция карты сайта ASP.NET 2.0 позволяет разработчику страниц определять карту сайта веб-приложения в некоторой постоянной среде, например в XML-файле. После определения данные карты сайта можно получить программным способом через SiteMap класс в System.Web пространстве имен или с помощью различных веб-элементов управления навигацией, таких как Элементы управления SiteMapPath, Menu и TreeView. Система карты сайта использует модель поставщика, чтобы можно было создавать различные реализации сериализации карты сайта и подключать их к веб-приложению. Поставщик карт сайта по умолчанию, поставляемый с ASP.NET 2.0, сохраняет структуру карты сайта в XML-файле. В руководстве по основным страницам и навигации по сайту мы создали файл с именем Web.sitemap , который содержал эту структуру и обновлял его XML с каждым новым разделом руководства.

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

Категории и продукты, которые составит структуру карты сайта

Рис. 1. Категории и Продукты составной части структуры карты сайта (щелкните для просмотра полноразмерного изображения)

Хотя эту структуру на основе категорий и продуктов можно жестко закодировать в Web.sitemap файл, файл необходимо обновлять каждый раз при добавлении, удалении или переименовании категории или продукта. Следовательно, обслуживание карты сайта было бы значительно упрощено, если ее структура была получена из базы данных или, в идеале, из уровня бизнес-логики архитектуры приложения. Таким образом, при добавлении, переименовании или удалении продуктов и категорий карта сайта будет автоматически обновляться, чтобы отразить эти изменения.

Так как сериализация карты сайта ASP.NET 2.0 s строится на основе модели поставщика, мы можем создать собственный пользовательский поставщик карты сайта, который получает данные из альтернативного хранилища данных, такого как база данных или архитектура. В этом руководстве мы создадим настраиваемый поставщик, который извлекает свои данные из BLL. Приступим к работе!

Примечание

Настраиваемый поставщик карт сайтов, созданный в этом руководстве, тесно связан с архитектурой и моделью данных приложения. Джефф Prosise в статьях Хранение карт сайтов в SQL Server и Поставщик карты сайта SQL, который вы ждали, рассматривают обобщенный подход к хранению данных карты сайта в SQL Server.

Шаг 1. Создание веб-страниц поставщика пользовательской карты сайта

Прежде чем приступить к созданию настраиваемого поставщика карт сайта, сначала добавим ASP.NET страниц, необходимых для работы с этим руководством. Начните с добавления новой папки с именем SiteMapProvider. Затем добавьте в папку следующие страницы ASP.NET, чтобы связать каждую страницу со страницей Site.master master:

  • Default.aspx
  • ProductsByCategory.aspx
  • ProductDetails.aspx

Также добавьте в CustomProviders папку вложенную папку App_Code .

Добавление страниц ASP.NET для учебников по Provider-Related карты сайта

Рис. 2. Добавление страниц ASP.NET для Provider-Related руководств по карте сайта

Так как для этого раздела существует только одно руководство, нам не нужно Default.aspx перечислять руководства по разделу. Вместо этого Default.aspx будут отображаться категории в элементе управления GridView. Мы рассмотрим эту проблему на шаге 2.

Затем обновите Web.sitemap , чтобы включить ссылку на страницу Default.aspx . В частности, добавьте следующую разметку после кэширования <siteMapNode>:

<siteMapNode 
    title="Customizing the Site Map" url="~/SiteMapProvider/Default.aspx" 
    description="Learn how to create a custom provider that retrieves the site map 
                 from the Northwind database." />

После обновления Web.sitemapпросмотрите веб-сайт учебников через браузер. Меню слева теперь содержит элемент для учебника по единственному поставщику карты сайта.

Карта сайта теперь включает запись для руководства по поставщику карты сайта

Рис. 3. Карта сайта теперь включает запись для руководства по поставщику карты сайта

В этом руководстве main основное внимание уделяется созданию пользовательского поставщика карт сайта и настройке веб-приложения для использования этого поставщика. В частности, мы создадим поставщика, который возвращает карту сайта, которая включает корневой узел вместе с узлом для каждой категории и продукта, как показано на рисунке 1. Как правило, каждый узел на карте сайта может указывать URL-адрес. Для карты сайта URL-адресом корневого узла будет ~/SiteMapProvider/Default.aspx, в котором будут перечислены все категории в базе данных. Каждый узел категории на карте сайта будет иметь URL-адрес, указывающий на , который будет выводить ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryIDсписок всех продуктов в указанном categoryID. Наконец, каждый узел карты сайта продукта будет указывать на ~/SiteMapProvider/ProductDetails.aspx?ProductID=productID, в котором будут отображаться сведения о конкретном продукте.

Для начала необходимо создать страницы Default.aspx, ProductsByCategory.aspxи ProductDetails.aspx . Эти страницы выполняются на шагах 2, 3 и 4 соответственно. Так как основное направление работы в этом руководстве относится к поставщикам карт сайта, а в предыдущих руководствах было описано создание таких многостраничных отчетов master/подробных отчетов, мы спешим выполнить шаги 2–4. Если вам требуется освежить создание master/подробных отчетов, охватывающих несколько страниц, ознакомьтесь с учебником Фильтрация основных и подробных данных на двух страницах.

Шаг 2. Отображение списка категорий

Откройте страницу Default.aspx в папке SiteMapProvider и перетащите Элемент GridView из панели элементов в Designer, задав для нее ID значение Categories. Из смарт-тега GridView привяжите его к новому объекту ObjectDataSource с именем CategoriesDataSource и настройте его таким образом, чтобы он извлек свои данные с помощью CategoriesBLL метода класса s GetCategories . Так как в этом GridView отображаются только категории и не предоставляются возможности изменения данных, задайте для раскрывающихся списков на вкладках UPDATE, INSERT и DELETE значение (Нет).

Настройка ObjectDataSource для возврата категорий с помощью метода GetCategories

Рис. 4. Настройка ObjectDataSource для возврата категорий с помощью GetCategories метода (щелкните для просмотра полноразмерного изображения)

Задайте для Drop-Down Списки на вкладках UPDATE, INSERT и DELETE значение (Нет)

Рис. 5. Задайте для Drop-Down Списки на вкладках UPDATE, INSERT и DELETE значение (Нет) (Щелкните для просмотра полноразмерного изображения)

После завершения работы мастера настройки источника данных Visual Studio добавит BoundField для CategoryID, CategoryName, Description, NumberOfProductsи BrochurePath. Измените GridView так, чтобы он содержал CategoryName только поля и Description BoundFields, и обновите CategoryName свойство BoundField на HeaderText Category .

Затем добавьте HyperLinkField и расположите его так, чтобы оно было самым левым полем. Задайте для свойства DataNavigateUrlFields значение CategoryID, а для свойства DataNavigateUrlFormatString — значение ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}. Присвойте свойству Text значение Просмотр продуктов .

Добавление HyperLinkField в gridView категорий

Рис. 6. Добавление HyperLinkField в Categories GridView

После создания ObjectDataSource и настройки полей GridView декларативная разметка двух элементов управления будет выглядеть следующим образом:

<asp:GridView ID="Categories" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:HyperLinkField DataNavigateUrlFields="CategoryID" 
            DataNavigateUrlFormatString=
                "~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}"
            Text="View Products" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" 
            SortExpression="Description" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}" SelectMethod="GetCategories" 
    TypeName="CategoriesBLL"></asp:ObjectDataSource>

На рисунке 7 показано Default.aspx при просмотре в браузере. Щелкнув ссылку Просмотреть продукты категории, вы перейдете к ProductsByCategory.aspx?CategoryID=categoryID, который мы создадим на шаге 3.

Каждая категория отображается вместе со ссылкой

Рис. 7. Каждая категория отображается вместе со ссылкой на просмотр продуктов (щелкните для просмотра полноразмерного изображения)

Шаг 3. Перечисление продуктов выбранной категории

Откройте страницу ProductsByCategory.aspx и добавьте Элемент GridView, назвав его ProductsByCategory. Из смарт-тега привяжите GridView к новому объекту ObjectDataSource с именем ProductsByCategoryDataSource. Настройте ObjectDataSource для использования ProductsBLL метода класса GetProductsByCategoryID(categoryID) и задайте для раскрывающихся списков значение (Нет) на вкладках UPDATE, INSERT и DELETE.

Использование метода GetProductsByCategoryID(categoryID) класса ProductsBLL

Рис. 8. Использование ProductsBLL метода Classs GetProductsByCategoryID(categoryID) (щелкните для просмотра полноразмерного изображения)

На последнем шаге мастера настройки источника данных запрашивается источник параметров для categoryID. Так как эти сведения передаются через поле CategoryIDquerystring , выберите QueryString в раскрывающемся списке и введите CategoryID в текстовом поле QueryStringField, как показано на рисунке 9. Чтобы завершить работу мастера, нажмите кнопку Готово.

Использование поля Строки запроса CategoryID для параметра categoryID

Рис. 9. Использование CategoryID поля querystring для параметра categoryID (щелкните для просмотра полноразмерного изображения)

После завершения работы мастера Visual Studio добавит соответствующие поля BoundFields и CheckBoxField в GridView для полей данных продукта. Удалите все поля, кроме ProductName, UnitPriceи SupplierName BoundFields. Настройте эти три свойства BoundFields HeaderText для чтения Product, Price и Supplier соответственно. Отформатируйте UnitPrice BoundField в виде валюты.

Затем добавьте HyperLinkField и переместите его в положение слева. Присвойте свойству Text значение Просмотреть сведения, свойству DataNavigateUrlFieldsProductID— значение , а свойству DataNavigateUrlFormatString — значение ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}.

Добавление гиперлинковых полей представления сведений, указывающих на ProductDetails.aspx

Рис. 10. Добавление гиперлинковых полей представления сведений, указывающих на ProductDetails.aspx

После выполнения этих настроек декларативная разметка GridView и ObjectDataSource должна выглядеть следующим образом:

<asp:GridView ID="ProductsByCategory" runat="server" AutoGenerateColumns="False"
    DataKeyNames="ProductID" DataSourceID="ProductsByCategoryDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:HyperLinkField DataNavigateUrlFields="ProductID" 
            DataNavigateUrlFormatString=
                "~/SiteMapProvider/ProductDetails.aspx?ProductID={0}"
            Text="View Details" />
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" 
            HeaderText="Price" HtmlEncode="False" 
            SortExpression="UnitPrice" />
        <asp:BoundField DataField="SupplierName" HeaderText="Supplier" 
            ReadOnly="True" SortExpression="SupplierName" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsByCategoryDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
    <SelectParameters>
        <asp:QueryStringParameter Name="categoryID" 
            QueryStringField="CategoryID" Type="Int32" />
    </SelectParameters>
</asp:ObjectDataSource>

Вернитесь к просмотру Default.aspx в браузере и щелкните ссылку Просмотреть продукты для напитков. Откроется ProductsByCategory.aspx?CategoryID=1раздел с именами, ценами и поставщиками продуктов в базе данных Northwind, которые относятся к категории "Напитки" (см. рис. 11). Вы можете дополнительно улучшить эту страницу, включив ссылку для возврата пользователей на страницу описания категорий (Default.aspx) и элемент управления DetailsView или FormView, который отображает имя и описание выбранной категории.

Отображаются названия напитков, цены и поставщики

Рис. 11. Отображаются названия напитков, цены и поставщики (щелкните для просмотра полноразмерного изображения)

Шаг 4. Отображение сведений о продукте

На последней ProductDetails.aspxстранице отображаются сведения о выбранных продуктах. Откройте ProductDetails.aspx элемент DetailsView и перетащите его с панели элементов на Designer. Задайте для свойства DetailsView значение ID и очистите его Height значения свойств и Width .ProductInfo Из смарт-тега привяжите DetailsView к новому объекту ObjectDataSource с именем ProductDataSource, настроив ObjectDataSource для извлечения данных из ProductsBLL метода класса .GetProductByProductID(productID) Как и на предыдущих веб-страницах, созданных на шагах 2 и 3, задайте для раскрывающихся списков на вкладках UPDATE, INSERT и DELETE значение (Нет) .

Настройка ObjectDataSource для использования метода GetProductByProductID(productID)

Рис. 12. Настройка ObjectDataSource для использования GetProductByProductID(productID) метода (щелкните для просмотра полноразмерного изображения)

На последнем шаге мастера настройки источника данных запрашивается источник параметра productID . Так как эти данные поступают через поле ProductIDquerystring , задайте в раскрывающемся списке значение QueryString, а для текстового поля QueryStringField — значение ProductID. Наконец, нажмите кнопку Готово, чтобы завершить работу мастера.

Настройка параметра productID для извлечения его значения из поля строки запроса ProductID

Рис. 13. Настройка параметра productID для извлечения его значения из ProductID поля строки запроса (щелкните для просмотра полноразмерного изображения)

После завершения работы мастера настройки источника данных Visual Studio создаст соответствующие поля BoundFields и CheckBoxField в detailsView для полей данных продукта. ProductIDУдалите , SupplierIDи CategoryID BoundFields и настройте остальные поля так, как вы считаете нужным. После нескольких эстетических конфигураций декларативная разметка DetailsView и ObjectDataSource выглядела следующим образом:

<asp:DetailsView ID="ProductInfo" runat="server" AutoGenerateRows="False" 
    DataKeyNames="ProductID" DataSourceID="ProductDataSource" 
    EnableViewState="False">
    <Fields>
        <asp:BoundField DataField="ProductName" HeaderText="Product" 
            SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            ReadOnly="True" SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName" HeaderText="Supplier" 
            ReadOnly="True" SortExpression="SupplierName" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit" 
            SortExpression="QuantityPerUnit" />
        <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" 
            HeaderText="Price" HtmlEncode="False" 
            SortExpression="UnitPrice" />
        <asp:BoundField DataField="UnitsInStock" HeaderText="Units In Stock" 
            SortExpression="UnitsInStock" />
        <asp:BoundField DataField="UnitsOnOrder" HeaderText="Units On Order" 
            SortExpression="UnitsOnOrder" />
        <asp:BoundField DataField="ReorderLevel" HeaderText="Reorder Level" 
            SortExpression="ReorderLevel" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" 
            SortExpression="Discontinued" />
    </Fields>
</asp:DetailsView>
<asp:ObjectDataSource ID="ProductDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProductByProductID" TypeName="ProductsBLL">
    <SelectParameters>
        <asp:QueryStringParameter Name="productID" 
            QueryStringField="ProductID" Type="Int32" />
    </SelectParameters>
</asp:ObjectDataSource>

Чтобы протестировать эту страницу, вернитесь на страницу Default.aspx и щелкните Просмотреть продукты для категории Напитки. В списке продуктов для напитков щелкните ссылку Просмотреть сведения для чая Чай. Откроется ProductDetails.aspx?ProductID=1раздел , в котором отображаются сведения о чайном чае (см. рис. 14).

Отображается поставщик, категория, цена и другая информация о чайной чае

Рис. 14. Отображается поставщик, категория, цена и другие сведения о чайном чае (щелкните, чтобы просмотреть полноразмерное изображение)

Шаг 5. Общие сведения о внутренней работе поставщика карты сайта

Карта сайта представлена в памяти веб-сервера в виде коллекции экземпляров SiteMapNode , образующих иерархию. Должен быть ровно один корневой узел, все некорновые узлы должны иметь ровно один родительский узел, а все узлы могут иметь произвольное количество дочерних узлов. Каждый SiteMapNode объект представляет раздел в структуре веб-сайта; эти разделы обычно имеют соответствующую веб-страницу. Следовательно, класс имеет такие свойства, SiteMapNode как Title, Urlи Description, которые предоставляют сведения для раздела, SiteMapNode который представляет . Существует также Key свойство, которое однозначно идентифицирует каждый из них SiteMapNode в иерархии, а также свойства, используемые для установления этой иерархии ChildNodes, ParentNode, NextSibling, PreviousSiblingи т. д.

На рисунке 15 показана общая структура карты сайта на рис. 1, но с подробными сведениями о реализации.

Каждый siteMapNode имеет такие свойства, как title, URL, key и т. д.

Рис. 15. Каждый из них SiteMapNode имеет такие свойства, как Title, Url, Keyи So On (щелкните для просмотра полноразмерного изображения)

Карта сайта доступна через SiteMap класс в System.Web пространстве имен . Это свойство класса RootNode возвращает корневой SiteMapNode экземпляр карты сайта; CurrentNode возвращает SiteMapNode свойство , свойство которого Url соответствует URL-адресу запрашиваемой страницы. Этот класс используется внутри веб-элементов управления навигации ASP.NET 2.0 с.

При доступе к свойствам SiteMap класса он должен сериализовать структуру карты сайта из некоторого постоянного носителя в память. Однако логика сериализации карты сайта не жестко закодирована в SiteMap классе . Вместо этого во время выполнения SiteMap класс определяет , какой поставщик карты сайта следует использовать для сериализации. По умолчанию XmlSiteMapProvider используется класс , который считывает структуру карты сайта из ПРАВИЛЬНО отформатированного XML-файла. Однако, немного поработав, мы можем создать собственный пользовательский поставщик карт сайта.

Все поставщики карт сайта должны быть производными от SiteMapProvider класса , который включает основные методы и свойства, необходимые для поставщиков карт сайта, но пропускает многие сведения о реализации. Второй класс, , StaticSiteMapProviderрасширяет SiteMapProvider класс и содержит более надежную реализацию необходимых функций. На внутреннем языке класс хранит SiteMapNode экземпляры карты сайта в Hashtable и предоставляет такие методы, StaticSiteMapProvider как AddNode(child, parent), RemoveNode(siteMapNode), и Clear() , которые добавляют и удаляют SiteMapNode во внутренний Hashtable. Класс XmlSiteMapProvider является производным от StaticSiteMapProvider.

При создании настраиваемого поставщика карты сайта, расширяющего StaticSiteMapProvider, необходимо переопределить два абстрактных метода: BuildSiteMap и GetRootNodeCore. BuildSiteMap, как следует из названия, отвечает за загрузку структуры карты сайта из постоянного хранилища и ее создание в памяти. GetRootNodeCore возвращает корневой узел в карте сайта.

Прежде чем веб-приложение сможет использовать поставщик карты сайта, оно должно быть зарегистрировано в конфигурации приложения. По умолчанию XmlSiteMapProvider класс регистрируется с помощью имени AspNetXmlSiteMapProvider. Чтобы зарегистрировать дополнительные поставщики карт сайта, добавьте следующую разметку в Web.config:

<configuration>
    <system.web>
        ...
        <siteMap defaultProvider="defaultProviderName">
          <providers>
            <add name="name" type="type" />
          </providers>
        </siteMap>
    </system.web>
</configuration>

Значение name присваивает поставщику понятное имя, а тип — полное имя типа поставщика карты сайта. Мы рассмотрим конкретные значения для имен и типов на шаге 7 после создания настраиваемого поставщика карт сайта.

Экземпляр класса поставщика карты сайта создается при первом обращении к нему из SiteMap класса и остается в памяти в течение всего времени существования веб-приложения. Так как существует только один экземпляр поставщика карты сайта, который может вызываться из нескольких одновременных посетителей веб-сайта, крайне важно, чтобы методы поставщика были потокобезопасными.

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

Примечание

При необходимости поставщик карты сайта может переопределить Initialize метод . Initialize вызывается при первом создании экземпляра поставщика карты сайта и передаче любых настраиваемых атрибутов, назначенных поставщику в Web.config элементе <add> , например: <add name="name" type="type" customAttribute="value" />. Это полезно, если вы хотите разрешить разработчику страниц указывать различные параметры, связанные с поставщиком карты сайта, без необходимости изменять код поставщика. Например, если мы считывали данные о категориях и продуктах непосредственно из базы данных, а не через архитектуру, скорее всего, мы хотели бы позволить разработчику страницы указать базу данных строка подключения черезWeb.config, а не использовать жестко закодированное значение в коде поставщика. Настраиваемый поставщик карты сайта, который мы создадим на шаге 6, не переопределяет этот Initialize метод. Пример использования Initialize метода см. в статье jeff Prosise'sStoring Site Maps в SQL Server статье.

Шаг 6. Создание настраиваемого поставщика карты сайта

Чтобы создать пользовательский поставщик карт сайта, который создает карту сайта из категорий и продуктов в базе данных Northwind, необходимо создать класс, расширяющий StaticSiteMapProvider. На шаге 1 я попросил вас добавить CustomProviders папку в папку App_Code — добавить новый класс в эту папку с именем NorthwindSiteMapProvider. Добавьте в класс NorthwindSiteMapProvider приведенный ниже код.

Imports System.Web
Imports System.Web.Caching
Public Class NorthwindSiteMapProvider
    Inherits StaticSiteMapProvider
    Private ReadOnly siteMapLock As New Object()
    Private root As SiteMapNode = Nothing
    Public Const CacheDependencyKey As String = "NorthwindSiteMapProviderCacheDependency"
    Public Overrides Function BuildSiteMap() As System.Web.SiteMapNode
        ' Use a lock to make this method thread-safe
        SyncLock siteMapLock
            ' First, see if we already have constructed the
            ' rootNode. If so, return it...
            If root IsNot Nothing Then
                Return root
            End If
            ' We need to build the site map!
            ' Clear out the current site map structure
            MyBase.Clear()
            ' Get the categories and products information from the database
            Dim productsAPI As New ProductsBLL()
            Dim products As Northwind.ProductsDataTable = productsAPI.GetProducts()
            ' Create the root SiteMapNode
            root = New SiteMapNode( _
                Me, "root", "~/SiteMapProvider/Default.aspx", "All Categories")
            AddNode(root)
            ' Create SiteMapNodes for the categories and products
            For Each product As Northwind.ProductsRow In products
                ' Add a new category SiteMapNode, if needed
                Dim categoryKey, categoryName As String
                Dim createUrlForCategoryNode As Boolean = True
                If product.IsCategoryIDNull() Then
                    categoryKey = "Category:None"
                    categoryName = "None"
                    createUrlForCategoryNode = False
                Else
                    categoryKey = String.Concat("Category:", product.CategoryID)
                    categoryName = product.CategoryName
                End If
                Dim categoryNode As SiteMapNode = FindSiteMapNodeFromKey(categoryKey)
                ' Add the category SiteMapNode if it does not exist
                If categoryNode Is Nothing Then
                    Dim productsByCategoryUrl As String = String.Empty
                    If createUrlForCategoryNode Then
                        productsByCategoryUrl = _
                            "~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=" & _
                            product.CategoryID
                    End If
                    categoryNode = New SiteMapNode _
                        (Me, categoryKey, productsByCategoryUrl, categoryName)
                    AddNode(categoryNode, root)
                End If
                ' Add the product SiteMapNode
                Dim productUrl As String = _
                    "~/SiteMapProvider/ProductDetails.aspx?ProductID=" & _
                    product.ProductID
                Dim productNode As New SiteMapNode _
                    (Me, String.Concat("Product:", product.ProductID), _
                    productUrl, product.ProductName)
                AddNode(productNode, categoryNode)
            Next
            ' Add a "dummy" item to the cache using a SqlCacheDependency
            ' on the Products and Categories tables
            Dim productsTableDependency As New _
                System.Web.Caching.SqlCacheDependency("NorthwindDB", "Products")
            Dim categoriesTableDependency As New _
                System.Web.Caching.SqlCacheDependency("NorthwindDB", "Categories")
            ' Create an AggregateCacheDependency
            Dim aggregateDependencies As New System.Web.Caching.AggregateCacheDependency()
            aggregateDependencies.Add(productsTableDependency, categoriesTableDependency)
            ' Add the item to the cache specifying a callback function
            HttpRuntime.Cache.Insert( _
                CacheDependencyKey, DateTime.Now, aggregateDependencies, _
                Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, _
                CacheItemPriority.Normal, AddressOf OnSiteMapChanged)
            ' Finally, return the root node
            Return root
        End SyncLock
    End Function
    Protected Overrides Function GetRootNodeCore() As System.Web.SiteMapNode
        Return BuildSiteMap()
    End Function
    Protected Sub OnSiteMapChanged _
    (key As String, value As Object, reason As CacheItemRemovedReason)
        SyncLock siteMapLock
            If String.Compare(key, CacheDependencyKey) = 0 Then
                ' Refresh the site map
                root = Nothing
            End If
        End SyncLock
    End Sub
    Public ReadOnly Property CachedDate() As Nullable(Of DateTime)
        Get
            Dim value As Object = HttpRuntime.Cache(CacheDependencyKey)
            If value Is Nothing OrElse Not TypeOf value Is Nullable(Of DateTime) Then
                Return Nothing
            Else
                Return CType(value, Nullable(Of DateTime))
            End If
        End Get
    End Property
End Class

Начнем с изучения этого метода класса BuildSiteMap , который начинается с lock оператора . Оператор lock разрешает вход только одному потоку за раз, тем самым сериализуя доступ к своему коду и предотвращая наступая двух параллельных потоков друг на друга.

Переменная root уровня SiteMapNode класса используется для кэширования структуры карты сайта. При создании карты сайта в первый раз или в первый раз после изменения root базовых данных будет Nothing иметь значение и будет создана структура карты сайта. Корневой узел карты сайта назначается root в процессе построения, поэтому при следующем вызове root этого метода не будет иметь значение Nothing. Следовательно, если root не является Nothing , структура карты сайта будет возвращена вызывающему объекту без необходимости воссоздать ее.

Если root имеет значение Nothing, структура карты сайта создается на основе сведений о продукте и категории. Схема сайта создается путем создания SiteMapNode экземпляров, а затем формирования иерархии с помощью вызовов StaticSiteMapProvider метода класса .AddNode AddNode выполняет внутреннюю бухгалтерию, сохраняя несколько SiteMapNode экземпляров в Hashtable. Прежде чем приступить к построению иерархии, мы начнем с вызова Clear метода , который очищает элементы из внутреннего Hashtable. ProductsBLL Затем метод класса GetProducts и результирующий ProductsDataTable объект хранятся в локальных переменных.

Создание карты сайта начинается с создания корневого узла и назначения его .root Перегрузка конструктора , используемогоSiteMapNode здесь, и во всем этом BuildSiteMap передается следующая информация:

  • Ссылка на поставщика карты сайта (Me).
  • Объект SiteMapNode s Key. Это обязательное значение должно быть уникальным для каждого SiteMapNode.
  • Объект SiteMapNode s Url. Url Является необязательным, но если указано, каждое SiteMapNodeUrl значение должно быть уникальным.
  • Объект SiteMapNode s Title, который является обязательным.

Вызов AddNode(root) метода добавляет в SiteMapNoderoot карту сайта в качестве корневого элемента. Затем перечисляется каждый ProductRow элемент в ProductsDataTable . Если для текущей SiteMapNode категории продукта уже существует , на него ссылается ссылка. В противном случае создается новый SiteMapNode объект для категории и добавляется в качестве дочернего элемента SiteMapNode``root для с помощью AddNode(categoryNode, root) вызова метода . После того как соответствующий узел категории SiteMapNode будет найден или создан, SiteMapNode создается для текущего продукта и добавляется в качестве дочернего элемента категории SiteMapNode через AddNode(productNode, categoryNode). Обратите внимание, что значение свойства категории SiteMapNodeUrl равно ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID , а свойству продукта SiteMapNode присвоено ~/SiteMapNode/ProductDetails.aspx?ProductID=productIDUrl значение .

Примечание

Те продукты, для CategoryID которых задано значение базы данныхNULL, сгруппированы в категориюSiteMapNode, для свойства которой TitleUrl задано значение None, а свойству — пустая строка. Я решил задать для пустой Url строки, так как ProductBLL метод класса в GetProductsByCategory(categoryID) настоящее время не имеет возможности возвращать только те продукты со значением NULLCategoryID . Кроме того, я хотел бы продемонстрировать, как элементы управления навигацией преобразовывают SiteMapNode объект , для свойства не имеет значения Url . Я призываю вас расширить этот учебник, чтобы свойство None SiteMapNode s указывает на ProductsByCategory.aspx, но отображалось только продукты со значениямиNULLCategoryID.Url

После создания карты сайта произвольный объект добавляется в кэш данных с помощью зависимости кэша SQL от Categories таблиц и Products через AggregateCacheDependency объект . Мы изучили использование зависимостей кэша SQL в предыдущем руководстве Использование зависимостей кэша SQL. Однако пользовательский поставщик карты сайта использует перегрузку метода кэша Insert данных, который нам еще предстоит изучить. Эта перегрузка принимает в качестве окончательного входного параметра делегат, который вызывается при удалении объекта из кэша. В частности, мы передадим новый CacheItemRemovedCallback делегат , указывающий на метод, определенный OnSiteMapChangedNorthwindSiteMapProvider далее в классе .

Примечание

Представление карты сайта в памяти кэшируется с помощью переменной rootуровня класса . Так как существует только один экземпляр пользовательского класса поставщика карты сайта и этот экземпляр является общим для всех потоков в веб-приложении, эта переменная класса служит кэшем. Метод BuildSiteMap также использует кэш данных, но только в качестве средства для получения уведомлений при изменении данных базовой Categories базы данных в таблицах или Products . Обратите внимание, что значение, помещенное в кэш данных, — это только текущая дата и время. Фактические данные карты сайта не помещают в кэш данных.

Метод BuildSiteMap завершается путем возврата корневого узла карты сайта.

Остальные методы довольно просты. GetRootNodeCore отвечает за возврат корневого узла. Так как BuildSiteMap возвращает корень, GetRootNodeCore просто возвращает BuildSiteMap возвращаемое значение . Метод OnSiteMapChanged возвращает root значение при Nothing удалении элемента кэша. Если для корневого каталога снова Nothingзадано значение , при BuildSiteMap следующем вызове структура карты сайта будет перестроена. Наконец, CachedDate свойство возвращает значение даты и времени, хранящиеся в кэше данных, если такое значение существует. Это свойство может использоваться разработчиком страницы, чтобы определить, когда данные карты сайта последний раз кэшировались.

Шаг 7. РегистрацияNorthwindSiteMapProvider

Чтобы наше веб-приложение использовало NorthwindSiteMapProvider поставщика карт сайта, созданного на шаге 6, необходимо зарегистрировать его в <siteMap> разделе Web.config. В частности, добавьте следующую разметку в элемент в <system.web>Web.config:

<siteMap defaultProvider="AspNetXmlSiteMapProvider">
  <providers>
    <add name="Northwind" type="NorthwindSiteMapProvider" />
  </providers>
</siteMap>

Эта разметка выполняет две задачи: во-первых, она указывает, что встроенный является поставщиком карты сайта по умолчанию; во-вторых AspNetXmlSiteMapProvider , она регистрирует настраиваемый поставщик карты сайта, созданный на шаге 6, с понятным для человека именем Northwind.

Примечание

Для поставщиков карт сайта, расположенных в папке приложения App_Code , значением атрибута type является просто имя класса. Кроме того, пользовательский поставщик карты сайта можно было создать в отдельном проекте библиотеки классов с скомпилированной сборкой, размещенной в каталоге /Bin веб-приложения. В этом случае значение атрибута type будет пространством имен.ClassName, AssemblyName .

После обновления Web.configуделите некоторое время просмотру любой страницы из учебников в браузере. Обратите внимание, что в интерфейсе навигации слева по-прежнему отображаются разделы и руководства, определенные в Web.sitemap. Это связано с тем, что мы оставили AspNetXmlSiteMapProvider в качестве поставщика по умолчанию. Чтобы создать элемент пользовательского интерфейса навигации, использующий NorthwindSiteMapProvider, необходимо явно указать, что должен использоваться поставщик карты сайта Northwind. Мы посмотрим, как это сделать, на шаге 8.

Шаг 8. Отображение сведений карты сайта с помощью поставщика пользовательской карты сайта

После создания и регистрации пользовательского поставщика карт сайта в Web.configмы готовы добавить элементы управления навигацией на Default.aspxстраницы , ProductsByCategory.aspxи ProductDetails.aspx в папке SiteMapProvider . Начните с открытия Default.aspx страницы и перетащите SiteMapPath элемент из панели элементов на Designer. Элемент управления SiteMapPath находится в разделе Навигация панели элементов.

Добавление SiteMapPath в Default.aspx

Рис. 16. Добавление SiteMapPath в Default.aspx (щелкните для просмотра полноразмерного изображения)

Элемент управления SiteMapPath отображает навигацию, указывающий расположение текущей страницы на карте сайта. Мы добавили SiteMapPath в верхнюю часть страницы master еще в руководстве По эталонным страницам и навигации по сайтам.

Просмотрите эту страницу в браузере. SiteMapPath, добавленный на рис. 16, использует поставщик карты сайта по умолчанию, извлекая данные из Web.sitemap. Таким образом, на панели навигации отображается домашняя > настройка карты сайта, как и в области навигации в правом верхнем углу.

В строке навигации используется поставщик карты сайта по умолчанию

Рис. 17. В строке навигации используется поставщик карты сайта по умолчанию (щелкните для просмотра полноразмерного изображения)

Чтобы добавить SiteMapPath на рис. 16, используйте настраиваемый поставщик карты сайта, созданный на шаге 6, задайте для его SiteMapProvider свойства Значение Northwind, которое мы назначили NorthwindSiteMapProvider в Web.config. К сожалению, Designer по-прежнему использует поставщика карт сайта по умолчанию, но если вы перейдете на страницу через браузер после изменения этого свойства, вы увидите, что теперь навигация использует настраиваемый поставщик карты сайта.

Снимок экрана: отображение пользовательского поставщика карты сайта в строке навигации.

Рис. 18. Теперь в строке навигации используется пользовательский поставщик NorthwindSiteMapProvider карты сайта (щелкните для просмотра полноразмерного изображения)

Элемент управления SiteMapPath отображает более функциональный пользовательский интерфейс на страницах ProductsByCategory.aspx и ProductDetails.aspx . Добавьте SiteMapPath на эти страницы, задав SiteMapProvider для свойства в обоих параметрах значение Northwind. Щелкните Default.aspx ссылку Просмотреть продукты для напитков, а затем щелкните ссылку Просмотреть сведения о чае чай. Как показано на рисунке 19, навигация включает текущий раздел карты сайта ( Чай ) и его предков: Напитки и Все категории .

Снимок экрана, показывающий, как на панели навигации отображается текущий раздел карты сайта (Чай Чай Чай) и его предки (Напитки и все категории).

Рис. 19. Теперь в строке навигации используется пользовательский поставщик NorthwindSiteMapProvider карты сайта (щелкните для просмотра полноразмерного изображения)

В дополнение к SiteMapPath можно использовать другие элементы пользовательского интерфейса навигации, например элементы управления Menu и TreeView. Страницы Default.aspx, ProductsByCategory.aspxи ProductDetails.aspx в скачивание этого руководства, например, включают элементы управления Меню (см. рис. 20). Дополнительные сведения об элементах управления навигацией и системе карты сайта в ASP.NET 2.0 см. в статье ASP.NET 2.0 в ASP.NET статье Расширенные функции навигации по сайту и в разделе Использование элементов управления навигацией сайта 2.0 .

Элемент управления меню Списки каждой из категорий и продуктов

Рис. 20. Элемент управления меню Списки каждой из категорий и продуктов (щелкните для просмотра полноразмерного изображения)

Как упоминалось ранее в этом руководстве, доступ к структуре карты сайта можно получить программным способом SiteMap с помощью класса . Следующий код возвращает корень SiteMapNode поставщика по умолчанию:

Dim root As SiteMapNode = SiteMap.RootNode

AspNetXmlSiteMapProvider Так как является поставщиком по умолчанию для нашего приложения, приведенный выше код вернет корневой узел, определенный в Web.sitemap. Чтобы создать ссылку на поставщика карты сайта, отличного от используемого SiteMap по умолчанию, используйте свойство класса Providers следующим образом:

Dim root As SiteMapNode = SiteMap.Providers("name").RootNode

Где name — это имя пользовательского поставщика карты сайта (Northwind, для нашего веб-приложения).

Чтобы получить доступ к члену, относяскомуся к поставщику карты сайта, используйте SiteMap.Providers["name"] для получения экземпляра поставщика, а затем приведите его к соответствующему типу. Например, чтобы отобразить свойство s CachedDate на NorthwindSiteMapProvider странице ASP.NET, используйте следующий код:

Dim customProvider As NorthwindSiteMapProvider = _
    TryCast(SiteMap.Providers("Northwind"), NorthwindSiteMapProvider)
If customProvider IsNot Nothing Then
    Dim lastCachedDate As Nullable(Of DateTime) = customProvider.CachedDate
    If lastCachedDate.HasValue Then
        SiteMapLastCachedDate.Text = _
            "Site map cached on: " & lastCachedDate.Value.ToString()
    Else
        SiteMapLastCachedDate.Text = "The site map is being reconstructed!"
    End If
End If

Примечание

Обязательно протестируйте функцию зависимостей кэша SQL. После посещения Default.aspxстраниц , ProductsByCategory.aspxи ProductDetails.aspx перейдите к одному из учебников в разделе Редактирование, Вставка и удаление и измените имя категории или продукта. Затем вернитесь на одну из страниц в папке SiteMapProvider . Если для механизма опроса прошло достаточно времени, чтобы заметить изменения в базовой базе данных, карта сайта должна быть обновлена для отображения нового названия продукта или категории.

Сводка

ASP.NET функций карты сайта 2.0 включает SiteMap класс, ряд встроенных веб-элементов управления навигацией и поставщик карты сайта по умолчанию, который ожидает, что данные карты сайта сохраняются в XML-файле. Чтобы использовать сведения карты сайта из другого источника, например из базы данных, архитектуры приложения или удаленной веб-службы, необходимо создать настраиваемый поставщик карт сайта. Это включает в себя создание класса, который прямо или косвенно является производным SiteMapProvider от класса .

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

Счастливого программирования!

Дополнительные материалы

Дополнительные сведения о темах, рассмотренных в этом руководстве, см. в следующих ресурсах:

Об авторе

Скотт Митчелл( Scott Mitchell), автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с веб-технологиями Майкрософт с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams Teach Yourself ASP.NET 2.0 в 24 часах. Он может быть доступен в mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.

Особая благодарность

Эта серия учебников была рассмотрена многими полезными рецензентами. Ведущим рецензентом этого руководства были Дэйв Гарднер, Зак Джонс, Тереса Мерфи и Бернадетт Ли. Хотите просмотреть предстоящие статьи MSDN? Если да, опустите мне строку на mitchell@4GuysFromRolla.com.