Создание пользовательского поставщика карт сайтов, управляемых базами данных (C#)
Поставщик карты сайта по умолчанию в 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
.
Рис. 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 значение (Нет) .
Рис. 4. Настройка ObjectDataSource для возврата категорий с помощью GetCategories
метода (щелкните для просмотра полноразмерного изображения)
Рис. 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
значение Просмотр продуктов .
Рис. 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.
Рис. 8. Использование ProductsBLL
метода Classs GetProductsByCategoryID(categoryID)
(щелкните для просмотра полноразмерного изображения)
На последнем шаге мастера настройки источника данных запрашивается источник параметров для categoryID. Так как эти сведения передаются через поле CategoryID
querystring , выберите QueryString в раскрывающемся списке и введите CategoryID в текстовое поле QueryStringField, как показано на рисунке 9. Чтобы завершить работу мастера, нажмите кнопку Готово.
Рис. 9. Использование CategoryID
поля querystring для параметра categoryID (щелкните для просмотра полноразмерного изображения)
После завершения работы мастера Visual Studio добавит соответствующие BoundFields и CheckBoxField в GridView для полей данных продукта. Удалите все поля ProductName
, кроме , UnitPrice
и SupplierName
BoundFields. Настройте эти три свойства BoundFields HeaderText
для чтения Product, Price и Supplier соответственно. Отформатируйте UnitPrice
BoundField как валюту.
Затем добавьте HyperLinkField и переместите его в самое левое положение. Присвойте свойству Text
значение Просмотр сведений, свойству DataNavigateUrlFields
— ProductID
значение , а свойству DataNavigateUrlFormatString
— значение ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}
.
Рис. 10. Добавление поля HyperLinkField сведений о представлении, указывающего на 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 значение (Нет) .
Рис. 12. Настройка ObjectDataSource для использования GetProductByProductID(productID)
метода (щелкните для просмотра полноразмерного изображения)
На последнем шаге мастера настройки источника данных запрашивается источник параметра productID . Так как эти данные поступают через поле ProductID
querystring , задайте в раскрывающемся списке значение QueryString, а для текстового поля QueryStringField — значение 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, но с подробными сведениями о реализации.
Рис. 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 ProsiseStoring Site Maps в SQL Server статье.
Шаг 6. Создание настраиваемого поставщика карты сайта
Чтобы создать пользовательский поставщик карт сайта, который создает карту сайта из категорий и продуктов в базе данных Northwind, необходимо создать класс, расширяющий StaticSiteMapProvider
. На шаге 1 я попросил вас добавить CustomProviders
папку в папку App_Code
— добавить новый класс в эту папку с именем NorthwindSiteMapProvider
. Добавьте в класс NorthwindSiteMapProvider
приведенный ниже код.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Caching;
public class NorthwindSiteMapProvider : StaticSiteMapProvider
{
private readonly object siteMapLock = new object();
private SiteMapNode root = null;
public const string CacheDependencyKey =
"NorthwindSiteMapProviderCacheDependency";
public override SiteMapNode BuildSiteMap()
{
// Use a lock to make this method thread-safe
lock (siteMapLock)
{
// First, see if we already have constructed the
// rootNode. If so, return it...
if (root != null)
return root;
// We need to build the site map!
// Clear out the current site map structure
base.Clear();
// Get the categories and products information from the database
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = productsAPI.GetProducts();
// Create the root SiteMapNode
root = new SiteMapNode(
this, "root", "~/SiteMapProvider/Default.aspx", "All Categories");
AddNode(root);
// Create SiteMapNodes for the categories and products
foreach (Northwind.ProductsRow product in products)
{
// Add a new category SiteMapNode, if needed
string categoryKey, categoryName;
bool createUrlForCategoryNode = true;
if (product.IsCategoryIDNull())
{
categoryKey = "Category:None";
categoryName = "None";
createUrlForCategoryNode = false;
}
else
{
categoryKey = string.Concat("Category:", product.CategoryID);
categoryName = product.CategoryName;
}
SiteMapNode categoryNode = FindSiteMapNodeFromKey(categoryKey);
// Add the category SiteMapNode if it does not exist
if (categoryNode == null)
{
string productsByCategoryUrl = string.Empty;
if (createUrlForCategoryNode)
productsByCategoryUrl =
"~/SiteMapProvider/ProductsByCategory.aspx?CategoryID="
+ product.CategoryID;
categoryNode = new SiteMapNode(
this, categoryKey, productsByCategoryUrl, categoryName);
AddNode(categoryNode, root);
}
// Add the product SiteMapNode
string productUrl =
"~/SiteMapProvider/ProductDetails.aspx?ProductID="
+ product.ProductID;
SiteMapNode productNode = new SiteMapNode(
this, string.Concat("Product:", product.ProductID),
productUrl, product.ProductName);
AddNode(productNode, categoryNode);
}
// Add a "dummy" item to the cache using a SqlCacheDependency
// on the Products and Categories tables
System.Web.Caching.SqlCacheDependency productsTableDependency =
new System.Web.Caching.SqlCacheDependency("NorthwindDB", "Products");
System.Web.Caching.SqlCacheDependency categoriesTableDependency =
new System.Web.Caching.SqlCacheDependency("NorthwindDB", "Categories");
// Create an AggregateCacheDependency
System.Web.Caching.AggregateCacheDependency aggregateDependencies =
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,
new CacheItemRemovedCallback(OnSiteMapChanged));
// Finally, return the root node
return root;
}
}
protected override SiteMapNode GetRootNodeCore()
{
return BuildSiteMap();
}
protected void OnSiteMapChanged(string key, object value, CacheItemRemovedReason reason)
{
lock (siteMapLock)
{
if (string.Compare(key, CacheDependencyKey) == 0)
{
// Refresh the site map
root = null;
}
}
}
public DateTime? CachedDate
{
get
{
return HttpRuntime.Cache[CacheDependencyKey] as DateTime?;
}
}
}
Начнем с изучения этого метода класса BuildSiteMap
, который начинается с lock
оператора . Оператор lock
разрешает вход только одному потоку за раз, тем самым сериализуя доступ к своему коду и предотвращая наступая двух параллельных потоков друг на друга.
Переменная root
уровня SiteMapNode
класса используется для кэширования структуры карты сайта. При создании карты сайта в первый раз или в первый раз после изменения root
базовых данных будет null
иметь значение и будет создана структура карты сайта. Корневой узел карты сайта назначается root
в процессе построения, поэтому при следующем вызове root
этого метода не будет иметь значение null
. Следовательно, если root
не является null
, структура карты сайта будет возвращена вызывающему объекту без необходимости воссоздать ее.
Если root имеет значение null
, структура карты сайта создается на основе сведений о продукте и категории. Схема сайта создается путем создания SiteMapNode
экземпляров, а затем формирования иерархии с помощью вызовов StaticSiteMapProvider
метода класса .AddNode
AddNode
выполняет внутреннюю бухгалтерию, сохраняя несколько SiteMapNode
экземпляров в Hashtable
. Прежде чем приступить к построению иерархии, мы начнем с вызова Clear
метода , который очищает элементы из внутреннего Hashtable
. ProductsBLL
Затем метод класса GetProducts
и результирующий ProductsDataTable
объект хранятся в локальных переменных.
Создание карты сайта начинается с создания корневого узла и назначения его .root
Перегрузка конструктора , используемогоSiteMapNode
здесь, и во всем этом BuildSiteMap
передается следующая информация:
- Ссылка на поставщика карты сайта (
this
). - Объект
SiteMapNode
sKey
. Это обязательное значение должно быть уникальным для каждогоSiteMapNode
. - Объект
SiteMapNode
sUrl
.Url
Является необязательным, но если указано, каждоеSiteMapNode
Url
значение должно быть уникальным. - Объект
SiteMapNode
sTitle
, который является обязательным.
Вызов AddNode(root)
метода добавляет в SiteMapNode
root
карту сайта в качестве корневого элемента. Затем перечисляется каждый ProductRow
элемент в ProductsDataTable
. Если для текущей SiteMapNode
категории продукта уже существует , на него ссылается ссылка. В противном случае создается новый SiteMapNode
объект для категории и добавляется в качестве дочернего элемента SiteMapNode``root
для с помощью AddNode(categoryNode, root)
вызова метода . После того как соответствующий узел категории SiteMapNode
будет найден или создан, SiteMapNode
создается для текущего продукта и добавляется в качестве дочернего элемента категории SiteMapNode
через AddNode(productNode, categoryNode)
. Обратите внимание, что значение свойства категории SiteMapNode
Url
равно ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
, а свойству продукта SiteMapNode
присвоено ~/SiteMapNode/ProductDetails.aspx?ProductID=productID
Url
значение .
Примечание
Те продукты, для CategoryID
которых задано значение базы данныхNULL
, сгруппированы в категориюSiteMapNode
, для свойства которой Title
Url
задано значение None, а свойству — пустая строка. Я решил задать для пустой Url
строки, так как ProductBLL
метод класса в GetProductsByCategory(categoryID)
настоящее время не имеет возможности возвращать только те продукты со значением NULL
CategoryID
. Кроме того, я хотел бы продемонстрировать, как элементы управления навигацией преобразовывают SiteMapNode
объект , для свойства не имеет значения Url
. Я призываю вас расширить этот учебник, чтобы свойство None SiteMapNode
s указывает на ProductsByCategory.aspx
, но отображалось только продукты со значениямиNULL
CategoryID
.Url
После создания карты сайта произвольный объект добавляется в кэш данных с помощью зависимости кэша SQL от Categories
таблиц и Products
через AggregateCacheDependency
объект . Мы изучили использование зависимостей кэша SQL в предыдущем руководстве Использование зависимостей кэша SQL. Однако пользовательский поставщик карты сайта использует перегрузку метода кэша Insert
данных, который нам еще предстоит изучить. Эта перегрузка принимает в качестве окончательного входного параметра делегат, который вызывается при удалении объекта из кэша. В частности, мы передадим новый CacheItemRemovedCallback
делегат , указывающий на метод, определенный OnSiteMapChanged
NorthwindSiteMapProvider
далее в классе .
Примечание
Представление карты сайта в памяти кэшируется с помощью переменной root
уровня класса . Так как существует только один экземпляр пользовательского класса поставщика карты сайта и этот экземпляр является общим для всех потоков в веб-приложении, эта переменная класса служит кэшем. Метод BuildSiteMap
также использует кэш данных, но только в качестве средства для получения уведомлений при изменении данных базовой Categories
базы данных в таблицах или Products
. Обратите внимание, что значение, помещенное в кэш данных, — это только текущая дата и время. Фактические данные карты сайта не помещают в кэш данных.
Метод BuildSiteMap
завершается путем возврата корневого узла карты сайта.
Остальные методы довольно просты. GetRootNodeCore
отвечает за возврат корневого узла. Так как BuildSiteMap
возвращает корень, GetRootNodeCore
просто возвращает BuildSiteMap
возвращаемое значение . Метод OnSiteMapChanged
возвращает root
значение при null
удалении элемента кэша. Если для корневого каталога снова null
задано значение , при 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 находится в разделе Навигация панели элементов.
Рис. 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
поставщика по умолчанию:
SiteMapNode root = SiteMap.RootNode;
AspNetXmlSiteMapProvider
Так как является поставщиком по умолчанию для нашего приложения, приведенный выше код вернет корневой узел, определенный в Web.sitemap
. Чтобы создать ссылку на поставщика карты сайта, отличного от используемого SiteMap
по умолчанию, используйте свойство класса Providers
следующим образом:
SiteMapNode root = SiteMap.Providers["name"].RootNode;
Где name — это имя пользовательского поставщика карты сайта (Northwind, для нашего веб-приложения).
Чтобы получить доступ к члену, относяскомуся к поставщику карты сайта, используйте SiteMap.Providers["name"]
для получения экземпляра поставщика, а затем приведите его к соответствующему типу. Например, чтобы отобразить свойство s CachedDate
на NorthwindSiteMapProvider
странице ASP.NET, используйте следующий код:
NorthwindSiteMapProvider customProvider =
SiteMap.Providers["Northwind"] as NorthwindSiteMapProvider;
if (customProvider != null)
{
DateTime? lastCachedDate = customProvider.CachedDate;
if (lastCachedDate != null)
LabelID.Text = "Site map cached on: " + lastCachedDate.Value.ToString();
else
LabelID.Text = "The site map is being reconstructed!";
}
Примечание
Обязательно протестируйте функцию зависимостей кэша SQL. После посещения Default.aspx
страниц , ProductsByCategory.aspx
и ProductDetails.aspx
перейдите к одному из учебников в разделе Редактирование, Вставка и удаление и измените имя категории или продукта. Затем вернитесь на одну из страниц в папке SiteMapProvider
. Если для механизма опроса прошло достаточно времени, чтобы заметить изменения в базовой базе данных, карта сайта должна быть обновлена для отображения нового названия продукта или категории.
Сводка
ASP.NET функций карты сайта 2.0 включает SiteMap
класс, ряд встроенных веб-элементов управления навигацией и поставщик карты сайта по умолчанию, который ожидает, что данные карты сайта сохраняются в XML-файле. Чтобы использовать сведения карты сайта из другого источника, например из базы данных, архитектуры приложения или удаленной веб-службы, необходимо создать настраиваемый поставщик карт сайта. Это включает в себя создание класса, который прямо или косвенно является производным SiteMapProvider
от класса .
В этом руководстве мы узнали, как создать настраиваемый поставщик карты сайта, который основан на информации о продукте и категории, выбираемой из архитектуры приложения. Наш поставщик расширил StaticSiteMapProvider
класс и повлек за собой BuildSiteMap
создание метода, который извлекал данные, создавал иерархию карты сайта и кэшировал итоговую структуру в переменной уровня класса. Мы использовали зависимость кэша SQL с функцией обратного вызова, чтобы сделать недействительной кэшированную структуру при изменении базовых Categories
данных или Products
данных.
Счастливого программирования!
Дополнительные материалы
Дополнительные сведения о темах, рассмотренных в этом руководстве, см. в следующих ресурсах:
- Хранение карт сайтов в SQL Server и поставщика карты сайта SQL, который вы ждали
- Набор средств поставщика
- Сложные функции навигации по сайтам ASP.NET 2.0
Об авторе
Скотт Митчелл( 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.
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по