Criação de um provedor de mapa de site personalizado controlado por banco de dados (C#)
por Scott Mitchell
O provedor de mapa do site padrão no ASP.NET 2.0 recupera seus dados de um arquivo XML estático. Embora o provedor baseado em XML seja adequado para muitos sites de pequeno e médio porte, os aplicativos Web maiores exigem um mapa de sites mais dinâmico. Neste tutorial, criaremos um provedor de mapa do site personalizado que recupera seus dados da Camada Lógica de Negócios, que, por sua vez, recupera dados do banco de dados.
Introdução
ASP.NET recurso de mapa do site do 2.0 permite que um desenvolvedor de página defina o mapa do site de um aplicativo Web em algum meio persistente, como em um arquivo XML. Depois de definidos, os dados do mapa do site podem ser acessados programaticamente por meio da SiteMap
classe no System.Web
namespace ou por meio de uma variedade de controles Web de navegação, como os controles SiteMapPath, Menu e TreeView. O sistema de mapa do site usa o modelo de provedor para que diferentes implementações de serialização do mapa do site possam ser criadas e conectadas a um aplicativo Web. O provedor de mapa do site padrão fornecido com o ASP.NET 2.0 mantém a estrutura do mapa do site em um arquivo XML. De volta ao tutorial Páginas mestras e navegação no site, criamos um arquivo chamado Web.sitemap
que continha essa estrutura e atualizamos seu XML a cada nova seção do tutorial.
O provedor de mapa do site baseado em XML padrão funcionará bem se a estrutura do mapa do site for bastante estática, como para esses tutoriais. Em muitos cenários, no entanto, é necessário um mapa do site mais dinâmico. Considere o mapa do site mostrado na Figura 1, onde cada categoria e produto aparecem como seções na estrutura do site. Com esse mapa do site, visitar a página da Web correspondente ao nó raiz pode listar todas as categorias, enquanto visitar a página da Web de uma categoria específica listaria os produtos dessa categoria e a exibição da página da Web de um produto específico mostraria os detalhes desse produto.
Figura 1: As categorias e os produtos compõem a estrutura do mapa do site (clique para exibir a imagem em tamanho real)
Embora essa estrutura baseada em categoria e produto possa ser codificada no Web.sitemap
arquivo, o arquivo precisaria ser atualizado sempre que uma categoria ou produto fosse adicionado, removido ou renomeado. Consequentemente, a manutenção do mapa do site seria bastante simplificada se sua estrutura fosse recuperada do banco de dados ou, idealmente, da Camada de Lógica de Negócios da arquitetura do aplicativo. Dessa forma, à medida que produtos e categorias fossem adicionados, renomeados ou excluídos, o mapa do site seria atualizado automaticamente para refletir essas alterações.
Como a serialização do mapa do site do ASP.NET 2.0 s é criada sobre o modelo de provedor, podemos criar nosso próprio provedor de mapa do site personalizado que captura seus dados de um armazenamento de dados alternativo, como o banco de dados ou a arquitetura. Neste tutorial, criaremos um provedor personalizado que recupera seus dados da BLL. Vamos começar!
Observação
O provedor de mapa do site personalizado criado neste tutorial está fortemente acoplado à arquitetura e ao modelo de dados do aplicativo. Os artigos Armazenando mapas de sites no SQL Server e O provedor de mapa de sites do SQL que você estava esperando examinam uma abordagem generalizada para armazenar dados de mapa de sites no SQL Server.
Etapa 1: Criando as páginas da Web personalizadas do provedor de mapa do site
Antes de começarmos a criar um provedor de mapa do site personalizado, vamos primeiro adicionar as páginas ASP.NET que precisaremos para este tutorial. Comece adicionando uma nova pasta chamada SiteMapProvider
. Em seguida, adicione as seguintes páginas ASP.NET a essa pasta, certificando-se de associar cada página à Site.master
página mestra:
Default.aspx
ProductsByCategory.aspx
ProductDetails.aspx
Adicione também uma CustomProviders
subpasta à App_Code
pasta.
Figura 2: Adicionar as páginas ASP.NET para os tutoriais relacionados ao provedor de mapa do site
Como há apenas um tutorial para esta seção, não precisamos Default.aspx
listar os tutoriais da seção. Em vez disso, Default.aspx
exibirá as categorias em um controle GridView. Abordaremos isso na Etapa 2.
Em seguida, atualize Web.sitemap
para incluir uma referência à Default.aspx
página. Especificamente, adicione a seguinte marcação após o Cache <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." />
Após a atualização Web.sitemap
, reserve um momento para visualizar o site de tutoriais por meio de um navegador. O menu à esquerda agora inclui um item para o tutorial do provedor de mapa do site exclusivo.
Figura 3: O mapa do site agora inclui uma entrada para o tutorial do provedor de mapa do site
O foco principal deste tutorial é ilustrar a criação de um provedor de mapa do site personalizado e a configuração de um aplicativo Web para usar esse provedor. Em particular, criaremos um provedor que retorna um mapa do site que inclui um nó raiz junto com um nó para cada categoria e produto, conforme ilustrado na Figura 1. Em geral, cada nó no mapa do site pode especificar uma URL. Para o mapa do site, a URL do nó raiz será ~/SiteMapProvider/Default.aspx
, que listará todas as categorias no banco de dados. Cada nó de categoria no mapa do site terá uma URL que aponta para ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
, que listará todos os produtos na categoryID especificada. Por fim, cada nó do mapa do site do produto apontará para ~/SiteMapProvider/ProductDetails.aspx?ProductID=productID
, que exibirá os detalhes específicos do produto.
Para começar, precisamos criar as Default.aspx
páginas , ProductsByCategory.aspx
, e ProductDetails.aspx
. Essas páginas são concluídas nas etapas 2, 3 e 4, respectivamente. Como o objetivo deste tutorial está nos provedores de mapas do site e como os tutoriais anteriores abordaram a criação desses tipos de relatórios mestres/detalhados de várias páginas, vamos nos apressar nas etapas 2 a 4. Se você precisar de uma atualização sobre a criação de relatórios mestre/detalhados que abrangem várias páginas, consulte o tutorial Filtragem mestre/detalhada em duas páginas .
Etapa 2: Exibindo uma lista de categorias
Abra a Default.aspx
página na SiteMapProvider
pasta e arraste um GridView da Caixa de Ferramentas para o Designer, definindo seu ID
Categories
como . Na marca inteligente do GridView, associe-a a um novo ObjectDataSource nomeado CategoriesDataSource
e configure-o para que ele recupere seus dados usando o CategoriesBLL
método da GetCategories
classe. Como esse GridView exibe apenas as categorias e não fornece recursos de modificação de dados, defina as listas suspensas nas guias UPDATE, INSERT e DELETE como (Nenhum) .
Figura 4: Configurar o ObjectDataSource para retornar categorias usando o método (clique para exibir a GetCategories
imagem em tamanho real)
Figura 5: Defina as listas suspensas nas guias UPDATE, INSERT e DELETE como (Nenhum) (clique para exibir a imagem em tamanho real)
Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio adicionará um BoundField para CategoryID
, CategoryName
, Description
, NumberOfProducts
e BrochurePath
. Edite o GridView para que ele contenha apenas o CategoryName
e Description
BoundFields e atualize a CategoryName
propriedade do BoundField HeaderText
para Category .
Em seguida, adicione um HyperLinkField e posicione-o de forma que seja o campo mais à esquerda. Defina a DataNavigateUrlFields
propriedade como CategoryID
e a DataNavigateUrlFormatString
propriedade como ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}
. Defina a Text
propriedade como Exibir produtos .
Figura 6: Adicionar um HyperLinkField ao Categories
GridView
Depois de criar o ObjectDataSource e personalizar os campos do GridView, a marcação declarativa dos dois controles será semelhante à seguinte:
<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>
A Figura 7 mostra Default.aspx
quando visualizada por meio de um navegador. Clicar no link Exibir produtos de uma categoria leva você a ProductsByCategory.aspx?CategoryID=categoryID
, que criaremos na Etapa 3.
Figura 7: Cada categoria é listada junto com um link Exibir produtos (clique para exibir a imagem em tamanho real)
Etapa 3: listar os produtos da categoria selecionada
Abra a ProductsByCategory.aspx
página e adicione um GridView, nomeando-o ProductsByCategory
. Em sua marca inteligente, associe o GridView a um novo ObjectDataSource chamado ProductsByCategoryDataSource
. Configure o ObjectDataSource para usar o ProductsBLL
método da GetProductsByCategoryID(categoryID)
classe e defina as listas suspensas como (Nenhum) nas guias UPDATE, INSERT e DELETE .
Figura 8: Usar o ProductsBLL
método da classe (GetProductsByCategoryID(categoryID)
clique para exibir a imagem em tamanho real)
A etapa final do assistente Configurar Fonte de Dados solicita uma fonte de parâmetro para categoryID. Como essas informações são passadas pelo campo CategoryID
querystring, selecione QueryString na lista suspensa e insira CategoryID na caixa de texto QueryStringField, como mostra a Figura 9. Clique em Concluir para concluir o assistente.
Figura 9: Usar o CategoryID
campo Querystring para o parâmetro categoryID (clique para exibir a imagem em tamanho real)
Depois de concluir o assistente, o Visual Studio adicionará BoundFields e um CheckBoxField correspondentes ao GridView para os campos de dados do produto. Remova todos, UnitPrice
exceto , ProductName
e SupplierName
BoundFields. Personalize essas três propriedades BoundFields HeaderText
para ler Produto, Preço e Fornecedor, respectivamente. Formate o UnitPrice
BoundField como uma moeda.
Em seguida, adicione um HyperLinkField e mova-o para a posição mais à esquerda. Defina sua Text
propriedade como Exibir detalhes, sua DataNavigateUrlFields
propriedade como ProductID
e sua DataNavigateUrlFormatString
propriedade como ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}
.
Figura 10: Adicionar um HyperLinkField de detalhes da exibição que aponta para ProductDetails.aspx
Depois de fazer essas personalizações, a marcação declarativa do GridView e ObjectDataSource deve ser semelhante ao seguinte:
<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>
Retorne à visualização Default.aspx
por meio de um navegador e clique no link Exibir produtos para bebidas. Isso o levará ao ProductsByCategory.aspx?CategoryID=1
, exibindo os nomes, preços e fornecedores dos produtos no banco de dados Northwind que pertencem à categoria Bebidas (consulte a Figura 11). Sinta-se à vontade para aprimorar ainda mais esta página para incluir um link para retornar os usuários à página de listagem de categorias (Default.aspx
) e um controle DetailsView ou FormView que exibe o nome e a descrição da categoria selecionada.
Figura 11: Os nomes, preços e fornecedores de bebidas são exibidos (clique para exibir a imagem em tamanho real)
Etapa 4: Mostrando os detalhes de um produto
A página final, ProductDetails.aspx
, exibe os detalhes dos produtos selecionados. Abra ProductDetails.aspx
e arraste um DetailsView da Caixa de Ferramentas para o Designer. Defina a propriedade DetailsView ID
como ProductInfo
e limpe seus Height
valores de propriedade e Width
. Em sua marca inteligente, associe o DetailsView a um novo ObjectDataSource chamado ProductDataSource
, configurando o ObjectDataSource para extrair seus dados do ProductsBLL
método da GetProductByProductID(productID)
classe. Assim como nas páginas da Web anteriores criadas nas Etapas 2 e 3, defina as listas suspensas nas guias UPDATE, INSERT e DELETE como (Nenhum) .
Figura 12: Configurar o ObjectDataSource para usar o método (clique para exibir a GetProductByProductID(productID)
imagem em tamanho real)
A última etapa do assistente Configurar Fonte de Dados solicita a origem do parâmetro productID . Como esses dados vêm por meio do campo ProductID
querystring, defina a lista suspensa como QueryString e a caixa de texto QueryStringField como ProductID. Por fim, clique no botão Concluir para concluir o assistente.
Figura 13: Configurar o parâmetro productID para extrair seu valor do campo Querystring (clique para exibir a ProductID
imagem em tamanho real)
Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio criará BoundFields e um CheckBoxField correspondentes no DetailsView para os campos de dados do produto. Remova os ProductID
SupplierID
, , e CategoryID
BoundFields e configure os campos restantes como achar melhor. Depois de algumas configurações estéticas, minha marcação declarativa DetailsView e ObjectDataSource ficou com a seguinte:
<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>
Para testar esta página, retorne e Default.aspx
clique em Exibir produtos para a categoria Bebidas. Na lista de produtos de bebidas, clique no link Ver detalhes do chá Chai. Isso o levará a ProductDetails.aspx?ProductID=1
, que mostra os detalhes de um chá Chai (consulte a Figura 14).
Figura 14: Fornecedor, categoria, preço e outras informações do chá Chai são exibidas (clique para exibir a imagem em tamanho real)
Etapa 5: Entendendo o funcionamento interno de um provedor de mapa do site
O mapa do site é representado na memória do servidor Web como uma coleção de SiteMapNode
instâncias que formam uma hierarquia. Deve haver exatamente uma raiz, todos os nós não raiz devem ter exatamente um nó pai e todos os nós podem ter um número arbitrário de filhos. Cada SiteMapNode
objeto representa uma seção na estrutura do site; essas seções geralmente têm uma página da Web correspondente. Consequentemente, a classe tem propriedades como Title
, Url
, e Description
, que fornecem informações para a seção que representaSiteMapNode
.SiteMapNode
Há também uma Key
propriedade que identifica exclusivamente cada um SiteMapNode
na hierarquia, bem como as propriedades usadas para estabelecer essa hierarquia ChildNodes
, ParentNode
, , NextSibling
PreviousSibling
e assim por diante.
A Figura 15 mostra a estrutura geral do mapa do site da Figura 1, mas com os detalhes da implementação esboçados em detalhes mais finos.
Figura 15: Cada um SiteMapNode
tem propriedades como Title
, Url
, Key
, e assim por diante (clique para exibir a imagem em tamanho real)
O mapa do site pode ser acessado por meio da SiteMap
classe no System.Web
namespace. A propriedade dessa RootNode
classe retorna a instância raiz SiteMapNode
do mapa do site; CurrentNode
retorna a SiteMapNode
propriedade whose Url
corresponde à URL da página solicitada no momento. Essa classe é usada internamente pelos controles Web de navegação do ASP.NET 2.0.
Quando as SiteMap
propriedades da classe são acessadas, ela deve serializar a estrutura do mapa do site de algum meio persistente na memória. No entanto, a lógica de serialização do mapa do site não é codificada na SiteMap
classe. Em vez disso, em runtime, a SiteMap
classe determina qual provedor de mapa do site usar para serialização. Por padrão, a XmlSiteMapProvider
classe é usada, que lê a estrutura do mapa do site de um arquivo XML formatado corretamente. No entanto, com um pouco de trabalho, podemos criar nosso próprio provedor de mapa do site personalizado.
Todos os provedores de mapa do site devem ser derivados da SiteMapProvider
classe, que inclui os métodos e propriedades essenciais necessários para provedores de mapa do site, mas omite muitos dos detalhes de implementação. Uma segunda classe, StaticSiteMapProvider
, estende a SiteMapProvider
classe e contém uma implementação mais robusta da funcionalidade necessária. Internamente, o StaticSiteMapProvider
armazena as SiteMapNode
instâncias do mapa do site em um Hashtable
e fornece métodos como AddNode(child, parent)
, RemoveNode(siteMapNode),
e Clear()
que adicionam e removem SiteMapNode
s para o .Hashtable
XmlSiteMapProvider
é derivado de StaticSiteMapProvider
.
Ao criar um provedor de mapa do site personalizado que estende StaticSiteMapProvider
o , há dois métodos abstratos que devem ser substituídos: BuildSiteMap
e GetRootNodeCore
. BuildSiteMap
, como o próprio nome indica, é responsável por carregar a estrutura do mapa do site do armazenamento persistente e construí-la na memória. GetRootNodeCore
Retorna o nó raiz no mapa do site.
Antes que um aplicativo Web possa usar um provedor de mapa do site, ele deve ser registrado na configuração do aplicativo. Por padrão, a XmlSiteMapProvider
classe é registrada usando o nome AspNetXmlSiteMapProvider
. Para registrar provedores de mapa do site adicionais, adicione a seguinte marcação a Web.config
:
<configuration>
<system.web>
...
<siteMap defaultProvider="defaultProviderName">
<providers>
<add name="name" type="type" />
</providers>
</siteMap>
</system.web>
</configuration>
O valor name atribui um nome legível ao provedor enquanto type especifica o nome de tipo totalmente qualificado do provedor de mapa do site. Exploraremos valores concretos para os valores de nome e tipo na Etapa 7, depois de criarmos nosso provedor de mapa do site personalizado.
A classe do provedor de mapa do site é instanciada na primeira vez que é acessada da SiteMap
classe e permanece na memória durante o tempo de vida do aplicativo Web. Como há apenas uma instância do provedor de mapa do site que pode ser invocada de vários visitantes simultâneos do site, é imperativo que os métodos do provedor sejam thread-safe.
Por motivos de desempenho e escalabilidade, é importante armazenar em cache a estrutura do mapa do site na memória e retornar essa estrutura armazenada em cache em vez de recriá-la sempre que o BuildSiteMap
método for invocado. BuildSiteMap
pode ser chamado várias vezes por solicitação de página por usuário, dependendo dos controles de navegação em uso na página e da profundidade da estrutura do mapa do site. De qualquer forma, se não armazenarmos em cache a estrutura BuildSiteMap
do mapa do site, cada vez que ela for invocada, precisaremos recuperar novamente as informações do produto e da categoria da arquitetura (o que resultaria em uma consulta ao banco de dados). Como discutimos nos tutoriais de cache anteriores, os dados armazenados em cache podem se tornar obsoletos. Para combater isso, podemos usar expirações baseadas em dependência de cache de tempo ou SQL.
Observação
Opcionalmente, um provedor de mapa do site pode substituir o Initialize
método. Initialize
é invocado quando o provedor de mapa do site é instanciado pela primeira vez e recebe todos os atributos personalizados atribuídos ao provedor no Web.config
<add>
elemento como: <add name="name" type="type" customAttribute="value" />
. É útil se você quiser permitir que um desenvolvedor de página especifique várias configurações relacionadas ao provedor de mapa do site sem precisar modificar o código do provedor. Por exemplo, se estivéssemos lendo os dados de categoria e produtos diretamente do banco de dados em vez de por meio da arquitetura, provavelmente desejaríamos permitir que o desenvolvedor da página especificasse a cadeia de conexão do banco de dados em Web.config
vez de usar um valor embutido em código no código do provedor. O provedor de mapa do site personalizado que criaremos na Etapa 6 não substitui esse Initialize
método. Para obter um exemplo de como usar o Initialize
método, consulte o artigo Armazenando mapas do site de Jeff Prosise no SQL Server.
Etapa 6: Criando o provedor de mapa do site personalizado
Para criar um provedor de mapa do site personalizado que cria o mapa do site a partir das categorias e produtos no banco de dados Northwind, precisamos criar uma classe que estenda StaticSiteMapProvider
. Na Etapa 1, pedi que você adicionasse uma CustomProviders
pasta na App_Code
pasta - adicione uma nova classe a esta pasta chamada NorthwindSiteMapProvider
. Adicione o código a seguir à classe 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?;
}
}
}
Vamos começar explorando o método dessa BuildSiteMap
classe, que começa com uma lock
instrução. A lock
instrução permite que apenas um thread por vez entre, serializando assim o acesso ao seu código e impedindo que dois threads simultâneos pisem nos dedos um do outro.
A variável root
de nível SiteMapNode
de classe é usada para armazenar em cache a estrutura do mapa do site. Quando o mapa do site for construído pela primeira vez, ou pela primeira vez após os dados subjacentes terem sido modificados, root
será null
e a estrutura do mapa do site será construída. O nó raiz do mapa do site é atribuído durante root
o processo de construção para que, na próxima vez que esse método for chamado, root
não seja null
. Consequentemente, desde que root
não null
seja, a estrutura do mapa do site será retornada ao chamador sem a necessidade de recriá-la.
Se a raiz for null
, a estrutura do mapa do site será criada a partir das informações do produto e da categoria. O mapa do site é criado criando as SiteMapNode
instâncias e, em seguida, formando a hierarquia por meio de chamadas para o StaticSiteMapProvider
método da AddNode
classe. AddNode
executa a contabilidade interna, armazenando as instâncias variadas SiteMapNode
em um Hashtable
. Antes de começarmos a construir a hierarquia, começamos chamando o Clear
método, que limpa os elementos do Hashtable
. Em seguida, o ProductsBLL
método da classe e GetProducts
o resultante ProductsDataTable
são armazenados em variáveis locais.
A construção do mapa do site começa criando o nó raiz e atribuindo-o a root
. A sobrecarga do SiteMapNode
construtor s usado aqui e ao longo disso BuildSiteMap
é passada as seguintes informações:
- Uma referência ao provedor de mapa do site (
this
). - O
SiteMapNode
sKey
. Esse valor necessário deve ser exclusivo para cadaSiteMapNode
. - O
SiteMapNode
sUrl
.Url
é opcional, mas, se fornecido, cadaSiteMapNode
valor sUrl
deve ser exclusivo. - O
SiteMapNode
sTitle
, que é necessário.
A AddNode(root)
chamada de método adiciona ao SiteMapNode
root
mapa do site como a raiz. Em seguida, cada ProductRow
um no ProductsDataTable
é enumerado. Se já existir um SiteMapNode
para a categoria do produto atual, ele será referenciado. Caso contrário, um novo SiteMapNode
para a categoria será criado e adicionado como filho da SiteMapNode``root
chamada through the AddNode(categoryNode, root)
method. Depois que o nó de categoria SiteMapNode
apropriado for encontrado ou criado, a SiteMapNode
é criado para o produto atual e adicionado como filho da categoria SiteMapNode
por meio AddNode(productNode, categoryNode)
de . Observe que o valor da propriedade da categoria SiteMapNode
é ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
enquanto a propriedade do Url
produto SiteMapNode
é atribuída~/SiteMapNode/ProductDetails.aspx?ProductID=productID
Url
.
Observação
Os produtos que têm um valor de banco de dados NULL
para seus CategoryID
são agrupados em uma categoria SiteMapNode
cuja Title
propriedade é definida como Nenhum e cuja Url
propriedade é definida como uma cadeia de caracteres vazia. Decidi definir Url
como uma cadeia de caracteres vazia, pois o ProductBLL
método da classe atualmente GetProductsByCategory(categoryID)
não tem a capacidade de retornar apenas os produtos com um NULL
CategoryID
valor. Além disso, eu queria demonstrar como os controles de navegação renderizam um SiteMapNode
que não tem um valor para sua Url
propriedade. Eu encorajo você a estender este tutorial para que a propriedade None SiteMapNode
Url
aponte para ProductsByCategory.aspx
, mas exiba apenas os produtos com NULL
CategoryID
valores.
Depois de construir o mapa do site, um objeto arbitrário é adicionado ao cache de dados usando uma dependência de cache SQL nas tabelas e Products
por meio de Categories
um AggregateCacheDependency
objeto. Exploramos o uso de dependências de cache SQL no tutorial anterior, Usando dependências de cache SQL. O provedor de mapa do site personalizado, no entanto, usa uma sobrecarga do método do Insert
cache de dados que ainda não exploramos. Essa sobrecarga aceita como parâmetro de entrada final um delegado que é chamado quando o objeto é removido do cache. Especificamente, passamos um novo CacheItemRemovedCallback
delegado que aponta para o OnSiteMapChanged
método definido mais abaixo na NorthwindSiteMapProvider
classe.
Observação
A representação na memória do mapa do site é armazenada em cache por meio da variável root
de nível de classe . Como há apenas uma instância da classe de provedor de mapa de site personalizada e como essa instância é compartilhada entre todos os threads no aplicativo Web, essa variável de classe serve como um cache. O BuildSiteMap
método também usa o cache de dados, mas apenas como um meio de receber notificação quando os Categories
dados do banco de dados subjacente nas tabelas ou Products
são alterados. Observe que o valor colocado no cache de dados é apenas a data e hora atuais. Os dados reais do mapa do site não são colocados no cache de dados.
O BuildSiteMap
método é concluído retornando o nó raiz do mapa do site.
Os métodos restantes são bastante simples. GetRootNodeCore
é responsável por retornar o nó raiz. Como BuildSiteMap
retorna a raiz, GetRootNodeCore
simplesmente retorna BuildSiteMap
o valor de retorno s. O OnSiteMapChanged
método é definido root
de volta para null
quando o item de cache é removido. Com a raiz definida de volta para null
, na próxima vez BuildSiteMap
que for invocada, a estrutura do mapa do site será recriada. Por fim, a CachedDate
propriedade retorna o valor de data e hora armazenado no cache de dados, se esse valor existir. Essa propriedade pode ser usada por um desenvolvedor de página para determinar quando os dados do mapa do site foram armazenados em cache pela última vez.
Etapa 7: Registrando oNorthwindSiteMapProvider
Para que nosso aplicativo da Web use o provedor de mapa do NorthwindSiteMapProvider
site criado na Etapa 6, precisamos registrá-lo na <siteMap>
seção de Web.config
. Especificamente, adicione a seguinte marcação dentro do <system.web>
elemento em Web.config
:
<siteMap defaultProvider="AspNetXmlSiteMapProvider">
<providers>
<add name="Northwind" type="NorthwindSiteMapProvider" />
</providers>
</siteMap>
Essa marcação faz duas coisas: primeiro, indica que o interno é o provedor de AspNetXmlSiteMapProvider
mapa do site padrão; segundo, registra o provedor de mapa do site personalizado criado na Etapa 6 com o nome amigável Northwind .
Observação
Para provedores de mapa do site localizados na pasta do App_Code
aplicativo, o valor do atributo é simplesmente o nome da type
classe. Como alternativa, o provedor de mapa do site personalizado pode ter sido criado em um projeto separado da Biblioteca de Classes com o assembly compilado colocado no diretório do /Bin
aplicativo Web. Nesse caso, o valor do type
atributo seria Namespace.ClassName, AssemblyName .
Após a atualização Web.config
, reserve um momento para exibir qualquer página dos tutoriais em um navegador. Observe que a interface de navegação à esquerda ainda mostra as seções e tutoriais definidos no Web.sitemap
. Isso ocorre porque deixamos AspNetXmlSiteMapProvider
como o provedor padrão. Para criar um elemento de interface do usuário de navegação que use o NorthwindSiteMapProvider
, precisaremos especificar explicitamente que o provedor de mapa do site Northwind deve ser usado. Veremos como fazer isso na Etapa 8.
Etapa 8: Exibindo informações do mapa do site usando o provedor de mapa do site personalizado
Com o provedor de mapa do site personalizado criado e registrado no Web.config
, estamos prontos para adicionar controles de navegação às Default.aspx
páginas , ProductsByCategory.aspx
e ProductDetails.aspx
na SiteMapProvider
pasta. Comece abrindo a Default.aspx
página e arraste a SiteMapPath
da Caixa de Ferramentas para o Designer. O controle SiteMapPath está localizado na seção Navegação da Caixa de Ferramentas.
Figura 16: Adicionar um SiteMapPath a Default.aspx
(clique para exibir a imagem em tamanho real)
O controle SiteMapPath exibe uma trilha de navegação, indicando o local da página atual no mapa do site. Adicionamos um SiteMapPath à parte superior da página mestra no tutorial Páginas mestras e navegação no site.
Reserve um momento para visualizar esta página por meio de um navegador. O SiteMapPath adicionado na Figura 16 usa o provedor de mapa do site padrão, extraindo seus dados do Web.sitemap
. Portanto, a trilha mostra Início > Personalizando o Mapa do Site, assim como a trilha no canto superior direito.
Figura 17: A trilha de navegação usa o provedor de mapa do site padrão (clique para exibir a imagem em tamanho real)
Para que o SiteMapPath seja adicionado na Figura 16, use o provedor de mapa do site personalizado que criamos na Etapa 6, defina sua SiteMapProvider
propriedade como Northwind, o nome que atribuímos ao NorthwindSiteMapProvider
arquivo .Web.config
Infelizmente, o Designer continua a usar o provedor de mapa do site padrão, mas se você visitar a página por meio de um navegador depois de fazer essa alteração de propriedade, verá que a trilha agora usa o provedor de mapa do site personalizado.
Figura 18: A trilha de navegação agora usa o provedor NorthwindSiteMapProvider
de mapa do site personalizado (clique para exibir a imagem em tamanho real)
O controle SiteMapPath exibe uma interface do usuário mais funcional nas ProductsByCategory.aspx
páginas e ProductDetails.aspx
. Adicione um SiteMapPath a essas páginas, definindo a SiteMapProvider
propriedade em ambas como Northwind. De Default.aspx
clique no link Exibir produtos para bebidas e, em seguida, no link Exibir detalhes para chá Chai. Como mostra a Figura 19, a trilha de navegação inclui a seção atual do mapa do site ( Chai Tea ) e seus ancestrais: Bebidas e Todas as Categorias .
Figura 19: A trilha de navegação agora usa o provedor NorthwindSiteMapProvider
de mapa do site personalizado (clique para exibir a imagem em tamanho real)
Outros elementos da interface do usuário de navegação podem ser usados além do SiteMapPath, como os controles Menu e TreeView. As Default.aspx
páginas , ProductsByCategory.aspx
e ProductDetails.aspx
no download deste tutorial, por exemplo, incluem controles Menu (consulte a Figura 20). Consulte os recursos sofisticados de navegação do site do ASP.NET 2.0 e a seção Usando controles de navegação do site dos Inícios rápidos do ASP.NET 2.0 para obter uma visão mais detalhada dos controles de navegação e do sistema de mapa do site no ASP.NET 2.0.
Figura 20: O controle Menu lista cada uma das categorias e produtos (clique para exibir a imagem em tamanho real)
Conforme mencionado anteriormente neste tutorial, a estrutura do mapa do site pode ser acessada programaticamente por meio da SiteMap
classe. O código a seguir retorna a raiz SiteMapNode
do provedor padrão:
SiteMapNode root = SiteMap.RootNode;
Como o AspNetXmlSiteMapProvider
é o provedor padrão para nosso aplicativo, o código acima retornaria o nó raiz definido no Web.sitemap
. Para fazer referência a um provedor de mapa do site diferente do padrão, use a SiteMap
propriedade da Providers
classe da seguinte forma:
SiteMapNode root = SiteMap.Providers["name"].RootNode;
Onde name é o nome do provedor de mapa do site personalizado (Northwind, para nosso aplicativo Web).
Para acessar um membro específico de um provedor de mapa do site, use SiteMap.Providers["name"]
para recuperar a instância do provedor e, em seguida, convertê-la no tipo apropriado. Por exemplo, para exibir a NorthwindSiteMapProvider
propriedade s CachedDate
em uma página ASP.NET, use o seguinte código:
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!";
}
Observação
Certifique-se de testar o recurso de dependência de cache SQL. Depois de visitar as Default.aspx
páginas , ProductsByCategory.aspx
, e , ProductDetails.aspx
vá para um dos tutoriais na seção Editando, Inserindo e Excluindo e edite o nome de uma categoria ou produto. Em seguida, retorne a uma das páginas da SiteMapProvider
pasta. Supondo que tenha passado tempo suficiente para que o mecanismo de sondagem observe a alteração no banco de dados subjacente, o mapa do site deve ser atualizado para mostrar o novo nome do produto ou da categoria.
Resumo
ASP.NET recursos do mapa do site do 2.0 s inclui uma SiteMap
classe, vários controles da Web de navegação internos e um provedor de mapa do site padrão que espera que as informações do mapa do site persistam em um arquivo XML. Para usar informações de mapa do site de alguma outra fonte, como de um banco de dados, da arquitetura do aplicativo ou de um serviço Web remoto, precisamos criar um provedor de mapa do site personalizado. Isso envolve a criação de uma classe que deriva, direta ou indiretamente, da SiteMapProvider
classe.
Neste tutorial, vimos como criar um provedor de mapa do site personalizado que baseou o mapa do site nas informações de produto e categoria selecionadas da arquitetura do aplicativo. Nosso provedor estendeu a classe e envolveu a StaticSiteMapProvider
criação de um BuildSiteMap
método que recuperou os dados, construiu a hierarquia do mapa do site e armazenou em cache a estrutura resultante em uma variável de nível de classe. Usamos uma dependência de cache SQL com uma função de retorno de chamada para invalidar a estrutura em cache quando os dados subjacentes Categories
ou Products
são modificados.
Boa programação!
Leitura Adicional
Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:
- Armazenando mapas de site no SQL Server e o provedor de mapa de site do SQL que você estava esperando
- O kit de ferramentas do provedor
- Recursos sofisticados de navegação do site do ASP.NET 2.0
Sobre o autor
Scott Mitchell, autor de sete livros ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Web da Microsoft desde 1998. Scott trabalha como consultor, instrutor e escritor independente. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 horas. Ele pode ser contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.
Agradecimentos especiais a
Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Dave Gardner, Zack Jones, Teresa Murphy e Bernadette Leigh. Interessado em revisar meus próximos artigos do MSDN? Em caso afirmativo, envie-me uma mensagem para mitchell@4GuysFromRolla.com.