Partilhar via


Criando um provedor de mapa do site Database-Driven personalizado (VB)

por Scott Mitchell

Descarregar PDF

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 do site mais dinâmico. Neste tutorial, criaremos um provedor de mapa do site personalizado que recupera seus dados da Camada de Lógica de Negócios, que, por sua vez, recupera dados do banco de dados.

Introdução

O recurso de mapa do site do ASP.NET 2.0 permite que um desenvolvedor de aplicações web defina o mapa do site de uma aplicação web em um meio persistente, como num ficheiro XML. Uma vez definidos, os dados do mapa do site podem ser acessados programaticamente por meio SiteMap da classe no System.Web namespace ou por meio de uma variedade de controles da 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 de mapa do site possam ser criadas e conectadas a um aplicativo Web. O provedor de mapa do site padrão que acompanha o ASP.NET 2.0 persiste 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 funciona 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 este 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 visualizar a página da Web de um determinado produto mostraria os detalhes do produto.

As categorias e produtos compõem a estrutura do mapa do site

Figura 1: As categorias e produtos compõem a estrutura do mapa do site (Clique para visualizar a imagem em tamanho real)

Embora a estrutura baseada em categorias e produtos pudesse estar codificada no arquivo Web.sitemap, este precisaria ser atualizado sempre que uma categoria ou produto fosse adicionado, removido ou renomeado. Consequentemente, a manutenção do mapa do site seria muito 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 é construída sobre o modelo do provedor, pode-se criar o nosso próprio provedor de mapa do site personalizado que captura os seus dados de uma solução de armazenamento alternativa, como a estrutura de dados ou o banco de dados. 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 de Jeff Prosise Storing Site Maps in SQL Server e The SQL Site Map Provider You've Been Waiting For examinam uma abordagem generalizada para armazenar dados de mapa de site no SQL Server.

Etapa 1: Criar as páginas Web do fornecedor de mapa do site personalizadas

Antes de começarmos a criar um provedor de mapa do site personalizado, vamos primeiro adicionar as ASP.NET páginas necessárias 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.

Adicionar as páginas ASP.NET para o Mapa do Sítio dos tutoriais Provider-Related

Figura 2: Adicionar as páginas de ASP.NET para o mapa do site nos tutoriais Provider-Related

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 Caching <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 através de um navegador. O menu à esquerda agora inclui um item para o tutorial do único provedor de mapa do site.

O mapa do site agora inclui uma entrada para o tutorial do provedor de mapa do site

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 especial, criaremos um fornecedor que retorne um mapa do site que inclua um nó raiz juntamente com um nó para cada categoria e produto, conforme mostrado na Figura 1. Em geral, cada nó no mapa do site pode especificar uma URL. Para o nosso mapa do site, o 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 no categoryID especificado. Finalmente, 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 páginas Default.aspx, ProductsByCategory.aspx e ProductDetails.aspx. Estas páginas são preenchidas nos Passos 2, 3 e 4, respetivamente. Como o foco deste tutorial está nos fornecedores de mapas do site, e como tutoriais anteriores abordaram a criação desses tipos de relatórios mestres/detalhados de várias páginas, vamos passar pelas Etapas 2 a 4. Se você precisar de uma atualização sobre a criação de relatórios mestres/detalhados que abrangem várias páginas, consulte novamente o tutorial Filtragem mestre/detalhe em duas páginas .

Etapa 2: Exibindo uma lista de categorias

Abra a página Default.aspx na pasta SiteMapProvider e arraste um GridView da Caixa de Ferramentas para o Designer, definindo a sua propriedade ID para Categories. A partir da marca inteligente de GridView, vincule-a a um novo ObjectDataSource chamado CategoriesDataSource e configure-o para que ele recupere seus dados usando o CategoriesBLL método s GetCategories de classe. Como este GridView apenas exibe as categorias e não fornece recursos de modificação de dados, configure as listas suspensas nas guias UPDATE, INSERT e DELETE como (NENHUM).

Configurar o ObjectDataSource para retornar categorias usando o método GetCategories

Figura 4: Configurar o ObjectDataSource para retornar categorias usando o método (GetCategories imagem em tamanho real)

Defina as listas de Drop-Down nas abas UPDATE, INSERT e DELETE como (Nenhuma)

Figura 5: Defina as listas de Drop-Down nas guias UPDATE, INSERT e DELETE como (Nenhuma) (Clique para visualizar a imagem em tamanho normal)

Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio adicionará um BoundField para CategoryID, CategoryName, Description, NumberOfProductse BrochurePath. Edite o GridView para que ele contenha apenas os BoundFields CategoryName e Description e atualize a propriedade CategoryName do BoundField HeaderText para Category.

Em seguida, adicione um HyperLinkField e posicione-o de modo que seja o campo mais à esquerda. Defina a DataNavigateUrlFields propriedade como CategoryID e a DataNavigateUrlFormatString propriedade como ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}. Defina a propriedade Text como Ver Produtos.

Adicionar um HyperLinkField ao GridView de categorias

Figura 6: Adicionar um HyperLinkField ao Categories GridView

Depois de criar o ObjectDataSource e personalizar os campos de GridView, a marcação declarativa dos dois controles terá a seguinte aparência:

<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 através de um navegador. Ao clicar no link Exibir Produtos de uma categoria, você será direcionado para o ProductsByCategory.aspx?CategoryID=categoryID, que será criado na Etapa 3.

Cada categoria é listada junto com um link Exibir produtos

Figura 7: Cada categoria é listada junto com um link Exibir produtos (Clique para visualizar a imagem em tamanho real)

Etapa 3: Listando os produtos da categoria selecionada

Abra a ProductsByCategory.aspx página e adicione um GridView, nomeando-o ProductsByCategory. A partir da sua etiqueta inteligente, ligue o GridView a um novo ObjectDataSource chamado ProductsByCategoryDataSource. Configure o ObjectDataSource para usar o método da classe ProductsBLL e defina as listas suspensas GetProductsByCategoryID(categoryID) como (Nenhum) nas abas UPDATE, INSERT e DELETE.

Use o método GetProductsByCategoryID(categoryID) da classe ProductsBLL

Figura 8: Usar o método Class s ProductsBLL (GetProductsByCategoryID(categoryID) 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, selecione QueryString na lista suspensa e digite CategoryID na caixa de texto QueryStringField, como mostra a Figura 9. Clique em Concluir para finalizar o assistente.

Use o campo cadeia de consulta CategoryID para o parâmetro categoryID

Figura 9: Use o campo Querystring para o parâmetro CategoryID (Clique para visualizar a imagem em tamanho real)

Depois de concluir o assistente, o Visual Studio adicionará os BoundFields correspondentes e um CheckBoxField ao GridView para os campos de dados do produto. Remova todos, exceto o ProductName, UnitPrice 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 View Details, sua DataNavigateUrlFields propriedade como ProductID, e sua DataNavigateUrlFormatString propriedade como ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}.

Adicionar um HyperLinkField para ver detalhes que aponta para ProductDetails.aspx

Figura 10: Adicionar um campo de hiperligação com o texto "Ver Detalhes" que aponta para ProductDetails.aspx

Depois de fazer essas personalizações, a marcação declarativa de GridView e ObjectDataSource deve ser semelhante à 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>

Volte a visualizar Default.aspx num navegador e clique no link Ver Produtos na categoria 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.

Os nomes, preços e fornecedores das bebidas são exibidos

Figura 11: Os nomes, preços e fornecedores de bebidas são exibidos (Clique para visualizar 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 para ID e limpe os valores das propriedades ProductInfo e Height. A partir da sua etiqueta inteligente, vincule o DetailsView a um novo ObjectDataSource chamado ProductDataSource, configurando o ObjectDataSource para extrair os seus dados do método ProductsBLL da classe GetProductByProductID(productID). Tal como acontece com as páginas web anteriores criadas nas etapas 2 e 3, defina as listas pendentes nos separadores UPDATE, INSERT e DELETE como (Nenhum).

Configurar o ObjectDataSource para usar o método GetProductByProductID(productID)

Figura 12: Configurar o ObjectDataSource para usar o método (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 através do campo ProductIDquerystring, defina a lista suspensa como QueryString e a caixa de texto QueryStringField como ProductID. Por fim, clique no botão Concluir para finalizar o assistente.

Configure o parâmetro productID para extrair seu valor do campo ProductID Querystring

Figura 13: Configurar o parâmetro productID para extrair seu valor do campo Querystring (ProductID imagem em tamanho real)

Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio criará BoundFields correspondentes e um CheckBoxField no DetailsView para os campos de dados do produto. Remova o ProductID, SupplierIDe CategoryID BoundFields e configure os campos restantes como achar melhor. Após algumas configurações estéticas, a minha marcação declarativa de DetailsView e ObjectDataSource ficou assim:

<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, volte para Default.aspx e clique em Ver Produtos na categoria de Bebidas. Na lista de produtos de bebidas, clique no link Ver detalhes do chá Chai. Isto irá levá-lo para ProductDetails.aspx?ProductID=1, que mostra os detalhes de um Chá Chai (ver Figura 14).

Chai Tea s fornecedor, categoria, preço, e outras informações são exibidas

Figura 14: Fornecedor, categoria, preço e outras informações da Chai Tea são exibidas (Clique para visualizar a imagem em tamanho real)

Etapa 5: Compreendendo o funcionamento interno de um provedor de mapa do site

O mapa do site é representado na memória do servidor Web como um conjunto 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 SiteMapNode classe tem propriedades como Title, Urle Description, que fornecem informações para a seção que representa SiteMapNode . Há também uma propriedade que identifica exclusivamente cada Key na hierarquia, bem como propriedades usadas para estabelecer essa hierarquia, SiteMapNode, ChildNodes, ParentNode, NextSibling, 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 com mais detalhes.

Cada SiteMapNode tem propriedades como Title, Url, Key e assim por diante

Figura 15: Cada SiteMapNode um tem propriedades como Title, Url, Keye assim por diante (Clique para visualizar a imagem em tamanho real)

O mapa do site é acessível através da SiteMap classe no System.Web namespace. A propriedade da classe RootNode retorna a instância raiz SiteMapNode do mapa do site; CurrentNode retorna a SiteMapNode cuja propriedade Url corresponde à URL da página atualmente solicitada. Essa classe é usada internamente pelos controles da Web de navegação do ASP.NET 2.0.

Quando as propriedades da SiteMap classe s 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 está codificada na classe SiteMap. Em vez disso, durante o tempo de execução, a SiteMap classe determina qual provedor do mapa do site usar para serialização. Por padrão, a XmlSiteMapProvider classe é usada, que lê a estrutura do mapa do site a partir 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 os provedores de mapa do site, mas omite muitos dos detalhes da implementação. Uma segunda classe, StaticSiteMapProvider, estende a SiteMapProvider classe e contém uma implementação mais robusta da funcionalidade necessária. Internamente, StaticSiteMapProvider armazena as instâncias do mapa do site em uma SiteMapNode e fornece métodos como Hashtable, AddNode(child, parent) e RemoveNode(siteMapNode), que adicionam e removem Clear() s ao SiteMapNode interno. XmlSiteMapProvider deriva de StaticSiteMapProvider.

Ao criar um provedor de mapa do site personalizado que estende StaticSiteMapProvider, 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 a partir 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 outros provedores de mapa do site, 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 por humanos 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 a partir da classe SiteMap e permanece na memória durante a duração do ciclo de vida da aplicação 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 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. Em qualquer caso, 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 isto, podemos usar expirações baseadas no tempo ou na dependência de cache SQL.

Observação

Um provedor de mapa do site pode, opcionalmente, substituir o Initialize método. Initialize é invocado quando o provedor de mapa do site é instanciado pela primeira vez e todos os atributos personalizados atribuídos ao provedor no elemento Web.config de <add> são passados, 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 ter que 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 através da arquitetura, provavelmente gostaríamos de 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 codificado 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 uso do Initialize método, consulte o artigo Storing Site Maps in SQL Server de Jeff Prosise.

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 se 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 seguinte código à NorthwindSiteMapProvider classe:

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

Vamos começar explorando o método desta BuildSiteMap classe, que começa com uma lock instrução. A lock instrução só permite que um thread de cada 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 de classe SiteMapNoderoot é usada para armazenar em cache a estrutura do mapa do site. Quando o mapa do site é construído pela primeira vez, ou pela primeira vez após os dados subjacentes terem sido modificados, root será Nothing e a estrutura do mapa do site será construída. O nó raiz do mapa do site é atribuído durante o processo de construção para root, para que, na próxima vez que este método for chamado, root não seja Nothing. Consequentemente, desde que root não seja Nothing, a estrutura do mapa do site será devolvida ao chamador sem ter que ser recriada.

Se root for igual a Nothing, a estrutura do mapa do site será criada a partir das informações do produto e da categoria. O mapa do site é construído criando as SiteMapNode instâncias e, em seguida, formando a hierarquia por meio de chamadas para o método StaticSiteMapProvider da classe AddNode. AddNode executa a contabilidade interna, armazenando as instâncias variadas SiteMapNode num Hashtable ficheiro. Antes de começarmos a construir a hierarquia, começamos chamando o Clear método, que limpa os elementos do interno Hashtable. Em seguida, o método ProductsBLL da classe GetProducts e o resultado ProductsDataTable são armazenados em variáveis locais.

O processo de construção do mapa do site começa com a criação do nó raiz, que é atribuído a root. A sobrecarga do SiteMapNode construtor usado aqui e ao longo deste BuildSiteMap é transmitida a seguinte informação:

  • Uma referência ao provedor de mapa do site (Me).
  • Os SiteMapNode são Key. Este valor necessário deve ser exclusivo para cada SiteMapNode.
  • Os SiteMapNode são Url. Url é opcional, mas se fornecido, cada SiteMapNode valor s Url deve ser exclusivo.
  • O SiteMapNode s Title, que é obrigatório.

A chamada de método AddNode(root) adiciona SiteMapNoderoot ao mapa do site como raiz. Em seguida, cada ProductRow um no ProductsDataTable é enumerado. Se já existir um SiteMapNode para a categoria de produto atual, ele é referenciado. Caso contrário, um novo SiteMapNode para a categoria é criado e adicionado como um filho da SiteMapNode``root chamada through the AddNode(categoryNode, root) method. Depois de encontrar ou criar o nó de categoria SiteMapNode apropriado, cria-se um SiteMapNode para o produto atual e este é adicionado como filho da categoria SiteMapNode via AddNode(productNode, categoryNode). Observe que o valor da propriedade da categoria SiteMapNode é Url enquanto a propriedade do produto ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryIDSiteMapNode é atribuída 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 está definida como Nenhum e cuja Url propriedade está definida como uma cadeia de caracteres vazia. Decidi definir Url para uma cadeia de caracteres vazia, uma vez que o ProductBLL método de classe s GetProductsByCategory(categoryID) atualmente não tem a capacidade de retornar apenas os produtos com um NULLCategoryID 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 Nenhum SiteMapNode s Url aponte para ProductsByCategory.aspx, mas só exiba os produtos com NULLCategoryID 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 Categories e Products através de um objeto AggregateCacheDependency. Exploramos o uso de dependências de cache SQL no tutorial anterior, Usando dependências de cache SQL. No entanto, o fornecedor de mapa do site personalizado usa uma sobrecarga do método de 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 método OnSiteMapChanged definido mais abaixo na classe NorthwindSiteMapProvider.

Observação

A representação na memória do mapa do site é armazenada em cache por meio da variável rootde nível de classe. Como há apenas uma instância da classe de provedor de mapa de site personalizado 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 dados do banco de dados subjacente nas Categories tabelas ou Products mudam. 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 método BuildSiteMap conclui devolvendo o nó raiz do mapa do site.

Os restantes métodos são bastante simples. GetRootNodeCore é responsável por retornar o nó raiz. Uma vez que BuildSiteMap retorna a raiz, GetRootNodeCore simplesmente retorna BuildSiteMap s valor de retorno. O OnSiteMapChanged método volta root para Nothing quando o item de cache é removido. Com a root restaurada para Nothing, da próxima vez que BuildSiteMap for invocado, a estrutura do mapa do site será reconstruída. 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.

Passo 7: Registar oNorthwindSiteMapProvider

Para que a nossa aplicação web use o provedor de mapa de site NorthwindSiteMapProvider criado na Etapa 6, precisamos registá-lo na seção <siteMap> 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 AspNetXmlSiteMapProvider integrado é o provedor de mapa do site padrão; em seguida, regista o provedor de mapa do site personalizado criado na Etapa 6 com o nome compreensível para humanos Northwind.

Observação

Para fornecedores de mapa do sítio localizados na pasta da aplicação App_Code, o valor do atributo é simplesmente o nome da classe type. Como alternativa, o provedor de mapa do site personalizado poderia ter sido criado num projeto separado de Biblioteca de Classes com o assembly compilado colocado no diretório da aplicação web /Bin. Nesse caso, o valor do type atributo seria Namespace.ClassName, AssemblyName .

Após a atualização Web.config, reserve um momento para visualizar qualquer página dos tutoriais em um navegador. Observe que a interface de navegação à esquerda ainda mostra as seções e tutoriais definidos em Web.sitemap. Isso ocorre porque deixámos 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, já estamos prontos para adicionar controles de navegação às páginas Default.aspx, ProductsByCategory.aspx e ProductDetails.aspx na pasta SiteMapProvider. Comece abrindo a página Default.aspx e arraste um SiteMapPath da Caixa de Ferramentas para no Designer. O controle SiteMapPath está localizado na seção Navegação da Caixa de Ferramentas.

Adicionar um SiteMapPath a Default.aspx

Figura 16: Adicionar um SiteMapPath a Default.aspx (Clique para ver a imagem em tamanho real)

O controlador SiteMapPath exibe uma trilha de navegação, indicando a localização atual da página no mapa do site. Adicionamos um SiteMapPath à parte superior da página mestra no tutorial Páginas mestras e navegação do site .

Reserve um momento para visualizar esta página através 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, o breadcrumb mostra Home > Customizing the Site Map, assim como o breadcrumb no canto superior direito.

A trilha usa o provedor de mapa do site padrão

Figura 17: A navegação hierárquica usa o provedor de mapa do site padrão (Clique para visualizar a imagem em tamanho completo)

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 in 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.

Captura de ecrã mostrando como a barra de navegação exibe o fornecedor de mapa do site personalizado.

Figura 18: A navegação agora usa o fornecedor de mapa do site personalizado NorthwindSiteMapProvider (Clique para exibir a imagem em tamanho real)

O controle SiteMapPath exibe uma interface de usuário mais funcional nas páginas ProductsByCategory.aspx e ProductDetails.aspx. Adicione um SiteMapPath a essas páginas, definindo a SiteMapProvider propriedade em ambas como Northwind. A partir de Default.aspx clique no link Ver produtos para bebidas e, em seguida, no link Ver detalhes para chá Chai. Como mostra a Figura 19, o pão ralado inclui a seção atual do mapa do site ( Chai Tea ) e seus ancestrais: Bebidas e Todas as Categorias.

Captura de tela mostrando como o pão ralado exibe a seção atual do mapa do site (Chai Tea) e seus ancestrais (Bebidas e Todas as categorias).

Figura 19: O link de navegação agora utiliza o fornecedor de mapa do site personalizado NorthwindSiteMapProvider (Clique para visualizar 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. O Default.aspx, ProductsByCategory.aspxe ProductDetails.aspx as páginas no download deste tutorial, por exemplo, incluem controles Menu (consulte a Figura 20). Consulte Recursos sofisticados de navegação no site do ASP.NET 2.0 e a seção Usando controles de navegação do site do ASP.NET 2.0 QuickStarts para obter uma visão mais detalhada dos controles de navegação e do sistema de mapa do site no ASP.NET 2.0.

O controle de menu lista cada uma das categorias e produtos

Figura 20: O controle de menu lista cada uma das categorias e produtos (Clique para visualizar a imagem em tamanho real)

Como mencionado anteriormente neste tutorial, a estrutura do mapa do site pode ser acessada programaticamente através da SiteMap classe. O código a seguir retorna a raiz SiteMapNode do provedor padrão:

Dim root As SiteMapNode = SiteMap.RootNode

Como o AspNetXmlSiteMapProvider é o provedor padrão para nosso aplicativo, o código acima retornaria o nó raiz definido em Web.sitemap. Para fazer referência a um provedor de mapa do site diferente do padrão, use a SiteMap da Providers classe da seguinte forma:

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

Onde nome é o nome do provedor de mapa do site personalizado (Northwind, para nossa aplicação 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 convertê-la para o tipo apropriado. Por exemplo, para exibir a propriedade NorthwindSiteMapProvider s CachedDate em uma página ASP.NET, use o seguinte código:

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

Observação

Certifique-se de testar o recurso de dependência de cache SQL. Depois de visitar as Default.aspx, ProductsByCategory.aspx e ProductDetails.aspx páginas, vá para um dos tutoriais na seção Edição, Inserção e Exclusão e edite o nome de uma categoria ou produto. Em seguida, volte para 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 nome do novo produto ou categoria.

Resumo

Os recursos de mapa do site do ASP.NET 2.0 incluem uma classe SiteMap, vários controlos de navegação Web incorporados e um fornecedor de mapa do site padrão que espera que as informações do mapa do site sejam guardadas num ficheiro 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 retiradas da arquitetura do aplicativo. Nosso provedor estendeu a StaticSiteMapProvider classe e implicou a criação de um BuildSiteMap método que recuperava os dados, construía a hierarquia do mapa do site e armazenava 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 armazenada em cache quando os dados subjacentes Categories ou Products são modificados.

Feliz Programação!

Leitura adicional

Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:

Sobre o Autor

Scott Mitchell, autor de sete livros sobre ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias Web da Microsoft desde 1998. Scott trabalha como consultor, formador e escritor independente. Seu último livro é Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Ele pode ser contatado em mitchell@4GuysFromRolla.com.

Um agradecimento especial 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 rever meus próximos artigos do MSDN? Se for o caso, envie-me uma mensagem para mitchell@4GuysFromRolla.com.