Delen via


Bouwen van een aanpasbare Database-Driven-sitemapprovider (C#)

door Scott Mitchell

PDF downloaden

De standaardsiteoverzichtprovider in ASP.NET 2.0 haalt de gegevens op uit een statisch XML-bestand. Hoewel de op XML gebaseerde provider geschikt is voor veel kleine en middelgrote websites, vereisen grotere webtoepassingen een dynamischere siteoverzicht. In deze zelfstudie bouwen we een aangepaste siteoverzichtprovider die de gegevens ophaalt uit de bedrijfslogicalaag, die op zijn beurt gegevens uit de database ophaalt.

Introductie

ASP.NET siteoverzichtfunctie van 2.0 stelt een paginaontwikkelaar in staat een siteoverzicht van een webtoepassing te definiëren in een permanent medium, zoals in een XML-bestand. Zodra deze zijn gedefinieerd, kunnen de siteoverzichtgegevens programmatisch worden geopend via de SiteMap klasse in de System.Web naamruimte of via verschillende navigatiewebbesturingselementen, zoals de besturingselementen SiteMapPath, Menu en TreeView. Het siteoverzichtsysteem maakt gebruik van het providermodel, zodat verschillende serialisatie-implementaties voor siteoverzichten kunnen worden gemaakt en in een webtoepassing kunnen worden aangesloten. De standaardsiteoverzichtprovider die wordt geleverd met ASP.NET 2.0, houdt de sitetoewijzingsstructuur vast in een XML-bestand. In de zelfstudie basispagina's en sitenavigatie hebben we een bestand gemaakt genaamd Web.sitemap, dat deze structuur bevat en hebben we de XML bijgewerkt met elke nieuwe sectie van de zelfstudie.

De standaard op XML gebaseerde siteoverzichtprovider werkt goed als de structuur van de siteoverzicht redelijk statisch is, zoals voor deze zelfstudies. In veel scenario's is echter een dynamischer siteoverzicht nodig. Bekijk het siteoverzicht dat wordt weergegeven in afbeelding 1, waarbij elke categorie en elk product worden weergegeven als secties in de websitestructuur. Met dit siteoverzicht kan een bezoek aan de webpagina die overeenkomt met het hoofdknooppunt alle categorieën weergeven, terwijl op een webpagina van een bepaalde categorie de producten van die categorie worden weergegeven en de details van een bepaald product worden weergegeven.

De categorieën en producten maken de structuur van de siteoverzicht

Afbeelding 1: De categorieën en producten vormen de structuur van het siteoverzicht (Klik om de volledige afbeelding weer te geven)

Hoewel deze categorie- en productstructuur in het Web.sitemap bestand in code kan worden vastgelegd, moet het bestand worden bijgewerkt telkens wanneer een categorie of product is toegevoegd, verwijderd of hernoemd. Daarom zou het onderhoud van de sitemap aanzienlijk worden vereenvoudigd als de structuur uit de database wordt opgehaald of, idealiter, van de bedrijfslaag van de logica binnen de architectuur van de applicatie. Op die manier wordt het siteoverzicht automatisch bijgewerkt naarmate producten en categorieën zijn toegevoegd, hernoemd of verwijderd, zodat deze wijzigingen worden weergegeven.

Omdat ASP.NET 2.0-siteoverzichtserialisatie boven op het providermodel is gebouwd, kunnen we onze eigen aangepaste siteoverzichtprovider maken die de gegevens uit een alternatief gegevensarchief haalt, zoals de database of architectuur. In deze zelfstudie bouwen we een aangepaste provider waarmee de gegevens van de BLL worden opgehaald. Laten we beginnen!

Opmerking

De aangepaste siteoverzichtprovider die in deze zelfstudie is gemaakt, is nauw gekoppeld aan de architectuur en het gegevensmodel van de toepassing. Jeff Prosise's artikelen Siteoverzichten opslaan in SQL Server en De SQL-siteoverzichtprovider waar u op heeft gewacht onderzoeken een gegeneraliseerde benadering voor het opslaan van siteoverzichtgegevens in SQL Server.

Stap 1: Het maken van de webpagina's van de aangepaste sitemapprovider.

Voordat we een aangepaste siteoverzichtprovider gaan maken, gaan we eerst de ASP.NET pagina's toevoegen die we nodig hebben voor deze zelfstudie. Begin met het toevoegen van een nieuwe map met de naam SiteMapProvider. Voeg vervolgens de volgende ASP.NET pagina's toe aan die map en zorg ervoor dat u elke pagina koppelt aan de Site.master basispagina:

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

Voeg ook een CustomProviders submap toe aan de App_Code map.

ASP.NET-pagina's toevoegen voor de Sitemap Provider-Related-handleidingen

Afbeelding 2: Voeg de ASP.NET-pagina's toe voor de tutorials van de sitemap Provider-Related.

Omdat er slechts één zelfstudie voor deze sectie is, hoeven we Default.aspx geen lijst met zelfstudies voor deze sectie weer te geven. In plaats daarvan worden de categorieën weergegeven in een GridView-besturingselement door Default.aspx. We gaan dit in stap 2 aanpakken.

Werk Web.sitemap vervolgens bij om een verwijzing naar de Default.aspx pagina op te nemen. Voeg met name de volgende markeringen toe na de 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." />

Neem na het bijwerken Web.sitemap even de moeite om de tutorialswebsite via een browser te bekijken. Het menu aan de linkerkant bevat nu een item voor de tutorial van de enige sitemap provider.

Het siteoverzicht bevat nu een vermelding voor de tutorial van de siteoverzicht provider

Afbeelding 3: Het siteoverzicht bevat nu een vermelding voor de handleiding siteoverzichtprovider

In deze zelfstudie richt u zich vooral op het maken van een aangepaste siteoverzichtprovider en het configureren van een webtoepassing voor het gebruik van die provider. Met name bouwen we een provider die een siteoverzicht retourneert dat een hoofdknooppunt bevat, samen met een knooppunt voor elke categorie en elk product, zoals wordt weergegeven in afbeelding 1. Over het algemeen kan elk knooppunt in het siteoverzicht een URL opgeven. Voor ons siteoverzicht is ~/SiteMapProvider/Default.aspxde URL van het hoofdknooppunt, waarin alle categorieën in de database worden vermeld. Elk categorieknooppunt in het siteoverzicht heeft een URL waarnaar verwijst ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID, waarin alle producten in de opgegeven categorie-id worden vermeld. Ten slotte verwijst elk knooppunt van de siteoverzicht van het product naar ~/SiteMapProvider/ProductDetails.aspx?ProductID=productID, waarmee de details van het specifieke product worden weergegeven.

Om te beginnen moeten we de Default.aspx, ProductsByCategory.aspxen ProductDetails.aspx pagina's maken. Deze pagina's worden voltooid in respectievelijk stap 2, 3 en 4. Aangezien de kern van deze zelfstudie betrekking heeft op sitekaartaanbieders en aangezien eerdere zelfstudies hebben behandeld het maken van dit soort meerbladige hoofd-/detailrapporten, zullen we snel doorlopen stap 2 tot en met 4. Als u een opfrisser nodig hebt voor het maken van hoofd-/detailrapporten die meerdere pagina's beslaan, raadpleegt u de tutorial Master/Detail Filtering over twee pagina's.

Stap 2: een lijst met categorieën weergeven

Open de Default.aspx pagina in de SiteMapProvider map en sleep een GridView van de Werkbalk naar de Ontwerper, waarbij u de ID instelt op Categories. Bind deze vanuit de infotag van GridView aan een nieuwe ObjectDataSource met de naam CategoriesDataSource en configureer deze, zodat de gegevens worden opgehaald met behulp van de CategoriesBLL klassemethode GetCategories . Aangezien deze GridView alleen de categorieën weergeeft en geen mogelijkheden biedt voor het wijzigen van gegevens, stelt u de vervolgkeuzelijsten in op de tabbladen UPDATE, INSERT en DELETE op (Geen).

De ObjectDataSource configureren om categorieën te retourneren met behulp van de methode GetCategories

Afbeelding 4: De ObjectDataSource configureren om categorieën te retourneren met behulp van de GetCategories methode (klik om de afbeelding op volledige grootte weer te geven)

Stel de Drop-Down-lijsten in de tabbladen UPDATE, INSERT en DELETE in op (Geen)

Afbeelding 5: Stel de Drop-Down-lijsten in de tabbladen UPDATE, INSERT en DELETE in op (Geen) (Klik om de volledige afbeelding weer te geven)

Nadat u de wizard Gegevensbron configureren hebt voltooid, wordt in Visual Studio een BoundField toegevoegd voor CategoryID, CategoryNameDescription, en . NumberOfProductsBrochurePath Bewerk de GridView zodat deze alleen de CategoryName en Description BoundFields bevat en de eigenschap van BoundField CategoryName bijwerken naar Categorie.

Voeg vervolgens een HyperLinkField toe en plaats het zo dat het het meest linkse veld is. Stel de eigenschap DataNavigateUrlFields in op CategoryID en de eigenschap DataNavigateUrlFormatString op ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}. Stel de Text eigenschap in op Producten weergeven.

Een HyperLinkField toevoegen aan de Categorieën Rasterweergave

Afbeelding 6: Een HyperLinkField toevoegen aan gridview Categories

Nadat u de ObjectDataSource hebt gemaakt en de velden van de GridView hebt aangepast, zal de declaratieve markering van de twee besturingselementen er als volgt uitzien:

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

Afbeelding 7 toont Default.aspx wanneer het wordt bekeken in een browser. Als u op de koppeling Producten weergeven van een categorie klikt, gaat u naar ProductsByCategory.aspx?CategoryID=categoryID, die we in stap 3 gaan bouwen.

Elke categorie wordt vermeld, samen met een koppeling Producten weergeven

Afbeelding 7: Elke categorie wordt vermeld, samen met een koppeling Producten weergeven (klik hier om de volledige afbeelding weer te geven)

Stap 3: de producten van de geselecteerde categorie weergeven

Open de ProductsByCategory.aspx pagina en voeg een GridView toe, waarbij u deze ProductsByCategoryeen naam geeft. Koppel de GridView vanuit de infotag aan een nieuwe ObjectDataSource met de naam ProductsByCategoryDataSource. Configureer de ObjectDataSource om de methode van de ProductsBLL-klasse te gebruiken en stel de vervolgkeuzelijsten in op (Geen) in de tabbladen UPDATE, INSERT en DELETE.

De methode ProductsBLL Class s GetProductsByCategoryID(categoryID) gebruiken

Afbeelding 8: Gebruik de methode van klasse ProductsBLLGetProductsByCategoryID(categoryID) (klik om de afbeelding op volledige grootte weer te geven)

De laatste stap in de wizard Gegevensbron configureren vraagt om een parameterbron voor categoryID. Omdat deze informatie wordt doorgegeven via het querytekenreeksveld CategoryID, selecteert u QueryString in de vervolgkeuzelijst en voert u CategoryID in het tekstvak QueryStringField in, zoals wordt weergegeven in afbeelding 9. Klik op Voltooien om de wizard te voltooien.

Het querytekenreeksveld CategoryID gebruiken voor de parameter categoryID

Afbeelding 9: Gebruik het CategoryID querytekenreeksveld voor de parameter categoryID (klik om de afbeelding op volledige grootte weer te geven)

Nadat de wizard is voltooid, voegt Visual Studio bijbehorende BoundFields en een CheckBoxField toe aan de GridView voor de productgegevensvelden. Verwijder alle, behalve de ProductName, UnitPriceen SupplierName BoundFields. Pas deze drie BoundFields-eigenschappen HeaderText aan om respectievelijk Product, Price en Leverancier te lezen. Maak het UnitPrice BoundField op als een valuta.

Voeg vervolgens een HyperLinkField toe en verplaats deze naar de meest linkse positie. Stel de Text eigenschap ervan in op View Details, de DataNavigateUrlFields eigenschap ervan op ProductID, en de DataNavigateUrlFormatString eigenschap ervan op ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}.

Een HyperLinkField voor details weergeven toevoegen die verwijst naar ProductDetails.aspx

Afbeelding 10: Voeg een HyperLinkField voor weergavedetails toe dat verwijst naar ProductDetails.aspx

Nadat u deze aanpassingen hebt gemaakt, moeten de declaratieve markeringen van GridView en ObjectDataSource er ongeveer als volgt uitzien:

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

Ga terug naar de weergave Default.aspx via een browser en klik op de koppeling Producten weergeven voor Dranken. Dit brengt u naar ProductsByCategory.aspx?CategoryID=1, waar de namen, prijzen en leveranciers van de producten in de Northwind-database worden getoond die deel uitmaken van de categorie Dranken (zie afbeelding 11). U kunt deze pagina verder verbeteren door een koppeling op te nemen om gebruikers terug te keren naar de pagina met categorievermeldingen (Default.aspx) en een besturingselement DetailsView of FormView waarin de naam en beschrijving van de geselecteerde categorie worden weergegeven.

De namen, prijzen en leveranciers van dranken worden weergegeven

Afbeelding 11: De namen, prijzen en leveranciers van dranken worden weergegeven (klik hier om de volledige afbeelding weer te geven)

Stap 4: Details van een product weergeven

Op de laatste pagina, ProductDetails.aspxworden de details van de geselecteerde producten weergegeven. Open ProductDetails.aspx en sleep een DetailsView van de Toolbox naar de Ontwerper. Stel de eigenschap van DetailsView in op ID naar ProductInfo en wis de waarden van de eigenschappen Height en Width. Koppel de DetailsView vanuit de slimme tag aan een nieuwe ObjectDataSource met de naam ProductDataSource, waarbij de ObjectDataSource configureert om de gegevens op te halen uit de ProductsBLL klasse via de GetProductByProductID(productID)-methode. Net als bij de vorige webpagina's die in stap 2 en 3 zijn gemaakt, stelt u de vervolgkeuzelijsten in op de tabbladen UPDATE, INSERT en DELETE op (Geen).

De ObjectDataSource configureren voor het gebruik van de methode GetProductByProductID(productID)

Afbeelding 12: De ObjectDataSource configureren om de GetProductByProductID(productID) methode te gebruiken (klik om de afbeelding op volledige grootte weer te geven)

De laatste stap van de wizard Gegevensbron configureren vraagt om de bron van de parameter product-id . Omdat deze gegevens via het querytekenreeksveld ProductIDworden verzonden, stelt u de vervolgkeuzelijst in op QueryString en het tekstvak QueryStringField op ProductID. Klik ten slotte op de knop Voltooien om de wizard te voltooien.

Configureer de parameter product-id om de waarde op te halen uit het querytekenreeksveld ProductID

Afbeelding 13: Configureer de parameter product-id om de waarde op te halen uit het ProductID querytekenreeksveld (klik om de volledige afbeelding weer te geven)

Nadat u de wizard Gegevensbron configureren hebt voltooid, maakt Visual Studio bijbehorende BoundFields en een CheckBoxField in de DetailsView voor de productgegevensvelden. Verwijder de ProductID, SupplierIDen CategoryID BoundFields en configureer de resterende velden naar wens. Na een handvol esthetische configuraties zag mijn DetailsView en ObjectDataSource's declaratieve opmaak er als volgt uit:

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

Als u deze pagina wilt testen, gaat u terug naar Default.aspx en klikt u op Producten voor de categorie Dranken weergeven. Klik in de lijst met drankproducten op de link Details weergeven voor Chai Tea. Hiermee gaat u naar ProductDetails.aspx?ProductID=1, waarin de details van een Chai Tea worden weergegeven (zie afbeelding 14).

Chai Thee leverancier, categorie, prijs en andere informatie wordt weergegeven

Afbeelding 14: Chai Thee leverancier, categorie, prijs en andere informatie wordt weergegeven (klik om de afbeelding volledig weer te geven)

Stap 5: inzicht krijgen in de interne werking van een siteoverzichtprovider

Het siteoverzicht wordt weergegeven in het geheugen van de webserver als een verzameling SiteMapNode exemplaren die een hiërarchie vormen. Er moet precies één hoofdknooppunt zijn, alle niet-hoofdknooppunten moeten precies één bovenliggend knooppunt hebben en alle knooppunten kunnen een willekeurig aantal onderliggende knooppunten hebben. Elk SiteMapNode object vertegenwoordigt een sectie in de websitestructuur. Deze secties hebben meestal een bijbehorende webpagina. SiteMapNode De klasse heeft dus eigenschappen zoals Title, Urlen Description, die informatie verstrekken voor de sectie die de SiteMapNode sectie vertegenwoordigt. Er is ook een Key eigenschap die elk SiteMapNode uniek identificeert in de hiërarchie, evenals eigenschappen die worden gebruikt om deze hiërarchie ChildNodes, ParentNode, NextSibling, PreviousSiblingenzovoort vast te stellen.

In afbeelding 15 ziet u de algemene structuur van de siteoverzicht uit afbeelding 1, maar met de details van de implementatie gedetailleerd weergegeven.

Elke SiteMapNode heeft eigenschappen zoals titel, URL, sleutel, enzovoort

Afbeelding 15: Elk SiteMapNode heeft eigenschappen zoals Title, Url, enzovoort Key(klik om de afbeelding op volledige grootte weer te geven)

Het siteoverzicht is toegankelijk via de SiteMap klasse in de System.Web naamruimte. Deze eigenschap van de klasse RootNode retourneert het hoofdexemplaar SiteMapNode van de sitemap; CurrentNode retourneert de SiteMapNode waarvan de Url eigenschap overeenkomt met de URL van de huidige pagina. Deze klasse wordt intern gebruikt door navigatiewebbesturingselementen van ASP.NET 2.0.

Wanneer toegang wordt verkregen tot de eigenschappen van de SiteMap klasse, moet de sitemapstructuur van een permanent medium in het geheugen worden geserialiseerd. De serialisatielogica voor siteoverzichten is echter niet in code vastgelegd in de SiteMap klasse. In plaats daarvan bepaalt de SiteMap klasse tijdens de uitvoering welke provider voor de sitemap moet worden gebruikt voor serialisatie. De klasse XmlSiteMapProvider wordt standaard gebruikt, waarmee de structuur van het siteoverzicht wordt gelezen uit een juist geformatteerd XML-bestand. Met een beetje werk kunnen we echter onze eigen aangepaste sitemapprovider maken.

Alle siteoverzichtproviders moeten worden afgeleid van de SiteMapProvider klasse, die de essentiële methoden en eigenschappen bevat die nodig zijn voor siteoverzichtproviders, maar veel van de implementatiedetails weglaat. Een tweede klasse, StaticSiteMapProviderbreidt de SiteMapProvider klasse uit en bevat een krachtigere implementatie van de benodigde functionaliteit. StaticSiteMapProvider Intern worden de SiteMapNode exemplaren van het siteoverzicht opgeslagen in een Hashtable en worden methoden geboden zoals AddNode(child, parent), RemoveNode(siteMapNode), en Clear() die s toevoegen en verwijderen SiteMapNode aan de interne Hashtable. XmlSiteMapProvider is afgeleid van StaticSiteMapProvider.

Bij het maken van een aangepaste sitemap-provider die uitbreidt naar StaticSiteMapProvider, moeten twee abstracte methoden worden overschreven: BuildSiteMap en GetRootNodeCore. BuildSiteMap, zoals de naam al aangeeft, is verantwoordelijk voor het laden van de sitemapstructuur uit permanente opslag en het samenstellen ervan in het geheugen. GetRootNodeCore retourneert het hoofdknooppunt in het siteoverzicht.

Voordat een webtoepassing een siteoverzichtprovider kan gebruiken, moet deze worden geregistreerd in de configuratie van de toepassing. De klasse wordt standaard XmlSiteMapProvider geregistreerd met de naam AspNetXmlSiteMapProvider. Als u extra siteoverzichtproviders wilt registreren, voegt u de volgende markeringen toe aan Web.config:

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

De naamwaarde wijst een door mensen leesbare naam toe aan de provider terwijl het type de volledig gekwalificeerde typenaam van de siteoverzichtprovider aangeeft. We verkennen concrete waarden voor de naam en typewaarden in stap 7 nadat we onze aangepaste siteoverzichtprovider hebben gemaakt.

De klasse siteoverzichtprovider wordt geïnstantieerd wanneer deze voor het eerst wordt geopend vanuit de SiteMap klasse en blijft gedurende de levensduur van de webtoepassing in het geheugen. Aangezien er slechts één exemplaar van de siteoverzichtprovider is die kan worden aangeroepen door meerdere, gelijktijdige websitebezoekers, is het noodzakelijk dat de providermethoden thread-safe zijn.

Om prestatie- en schaalbaarheidsredenen is het belangrijk dat we de structuur van de sitetoewijzing in het geheugen opslaan en deze structuur in de cache retourneren in plaats van deze opnieuw te maken telkens wanneer de BuildSiteMap methode wordt aangeroepen. BuildSiteMap kan meerdere keren per paginaaanvraag per gebruiker worden aangeroepen, afhankelijk van de navigatiebesturingselementen die worden gebruikt op de pagina en de diepte van de structuur van de siteoverzicht. In elk geval, als we de structuur van de siteoverzicht niet in BuildSiteMap de cache opslaan, moeten we telkens wanneer deze wordt aangeroepen, de product- en categoriegegevens opnieuw ophalen uit de architectuur (wat zou resulteren in een query naar de database). Zoals we in de vorige zelfstudies voor caching hebben besproken, kunnen gegevens in de cache verlopen. Om dit te bestrijden, kunnen we vervaldata gebruiken die gebaseerd zijn op tijd- of SQL-cache-afhankelijkheden.

Opmerking

Een sitemapprovider kan eventueel de Initialize methode overschrijven. Initialize wordt aangeroepen wanneer de siteoverzichtprovider voor het eerst wordt geïnstantieerd. Eventuele aangepaste kenmerken die aan de provider zijn toegewezen, worden doorgegeven in het Web.config-element in <add>, zoals: <add name="name" type="type" customAttribute="value" />. Het is handig als u een paginaontwikkelaar wilt toestaan om verschillende instellingen voor siteoverzichtproviders op te geven zonder dat u de code van de provider hoeft te wijzigen. Als we bijvoorbeeld de categorie- en productgegevens rechtstreeks vanuit de database lezen in plaats van via de architectuur, willen we de paginaontwikkelaar de databaseverbindingsreeks laten opgeven in Web.config plaats van een in code vastgelegde waarde in de code van de provider te gebruiken. De aangepaste siteoverzichtprovider die we in stap 6 gaan bouwen, overschrijft deze Initialize methode niet. Voor een voorbeeld van het gebruik van de Initialize methode raadpleegt u Jeff Prosise'sOpslag van siteoverzichten in SQL Server .

Stap 6: De aangepaste sitemapprovider maken

Als u een aangepaste siteoverzichtprovider wilt maken die het siteoverzicht bouwt op basis van de categorieën en producten in de Northwind-database, moeten we een klasse maken die uitbreidt StaticSiteMapProvider. In stap 1 heb ik u gevraagd een CustomProviders map toe te voegen aan de App_Code map . Voeg een nieuwe klasse toe aan deze map met de naam NorthwindSiteMapProvider. Voeg de volgende code toe aan de klasse 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?;
        }
    }
}

Laten we beginnen met het verkennen van deze klassemethode BuildSiteMap , die begint met een lock instructie. Met de lock instructie kan slechts één thread tegelijk worden ingevoerd, waardoor de toegang tot de code wordt geserialiseerd en wordt voorkomen dat twee gelijktijdige threads op elkaars tenen worden getrapt.

De variabele SiteMapNode op klasseniveau root wordt gebruikt om de structuur van de sitemap te cachen. Wanneer het siteoverzicht voor het eerst wordt samengesteld, of voor de eerste keer nadat de onderliggende gegevens zijn gewijzigd, zal root worden toegepast en dan wordt de structuur van het siteoverzicht samengesteld. Het hoofdknooppunt van het siteoverzicht wordt toegewezen aan root tijdens het bouwproces, zodat de volgende keer dat die methode wordt aangeroepen, root niet null zal zijn. Dus zolang root niet null is, wordt de structuur van de sitemap naar de aanroeper teruggezet zonder het opnieuw te hoeven maken.

Als de hoofdmap null is, wordt de structuur van de sitemap gemaakt op basis van de product- en categoriegegevens. Het siteoverzicht wordt gebouwd door de SiteMapNode exemplaren te maken en vervolgens de hiërarchie te vormen via aanroepen naar de StaticSiteMapProvider klassemethode AddNode . AddNode voert de interne boekhouding uit en slaat de verschillende SiteMapNode instanties op in een Hashtable. Voordat we beginnen met het bouwen van de hiërarchie, beginnen we met het aanroepen van de Clear methode, waarmee de elementen van het interne Hashtableelement worden gewist. Vervolgens worden de ProductsBLL klassemethode GetProducts en de resulterende ProductsDataTable methode opgeslagen in lokale variabelen.

De constructie van het siteoverzicht begint met het aanmaken van het hoofdknooppunt en het toewijzen hiervan aan root. De overbelasting van de SiteMapNode hier gebruikte constructor wordt BuildSiteMap doorgegeven aan de volgende informatie:

  • Een verwijzing naar de sitemapprovider (this).
  • De SiteMapNode s Key. Deze vereiste waarde moet uniek zijn voor elke SiteMapNodewaarde.
  • De SiteMapNode s Url. Url is optioneel, maar indien opgegeven, moet elke SiteMapNode waarde Url uniek zijn.
  • De SiteMapNode s Title, die vereist zijn.

De AddNode(root) methode-aanroep voegt de SiteMapNoderoot aan het siteoverzicht toe als de root. Vervolgens wordt elk ProductRow in de ProductsDataTable lijst opgesomd. Als er al een SiteMapNode voor de huidige productcategorie bestaat, wordt ernaar verwezen. Anders wordt er een nieuwe SiteMapNode voor de categorie gecreëerd en toegevoegd als onderliggend element aan de SiteMapNode``root via de AddNode(categoryNode, root)-methodeaanroep. Nadat het juiste categorieknooppunt SiteMapNode is gevonden of gemaakt, wordt er een SiteMapNode gemaakt voor het huidige product en toegevoegd als onderliggend element van de categorie SiteMapNode via AddNode(productNode, categoryNode). Houd er rekening mee dat de eigenschapswaarde van de categorie SiteMapNodeUrl is, terwijl de eigenschap ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID van het product SiteMapNode is toegewezen aan Url.

Opmerking

Producten met een databasewaarde NULL voor hun CategoryID producten worden gegroepeerd onder een categorie SiteMapNode waarvan Title de eigenschap is ingesteld op Geen en waarvan Url de eigenschap is ingesteld op een lege tekenreeks. Ik heb besloten om Url in te stellen op een lege tekenreeks, omdat de ProductBLL klasse de methode van GetProductsByCategory(categoryID) momenteel niet de mogelijkheid biedt om alleen die producten met een NULLCategoryID waarde terug te geven. Ik wilde ook laten zien hoe de navigatiebesturingselementen een SiteMapNode weergeven die geen waarde heeft voor de Url eigenschap. Ik moedig u aan deze zelfstudie uit te breiden zodat de eigenschap None SiteMapNode s verwijst naarUrl, maar alleen de producten met ProductsByCategory.aspxNULLCategoryID waarden worden weergegeven.

Nadat u het siteoverzicht hebt gemaakt, wordt een willekeurig object aan de gegevenscache toegevoegd met behulp van een SQL-cacheafhankelijkheid van de Categories en Products tabellen via een AggregateCacheDependency object. We hebben in de vorige zelfstudie verkend hoe sql-cacheafhankelijkheden worden gebruikt, met behulp van SQL Cache-afhankelijkheden. De aangepaste siteoverzichtprovider maakt echter gebruik van een overbelasting van de Insert gegevenscache methode die we nog moeten onderzoeken. Deze overbelasting accepteert als laatste invoerparameter een gemachtigde die wordt aangeroepen wanneer het object uit de cache wordt verwijderd. We geven een nieuwe CacheItemRemovedCallback delegate door die verwijst naar de OnSiteMapChanged methode die verderop in de NorthwindSiteMapProvider klasse is gedefinieerd.

Opmerking

De in-memory weergave van het siteoverzicht wordt in de cache opgeslagen via de variabele rootop klasseniveau. Omdat er slechts één exemplaar van de klasse aangepaste siteoverzichtprovider is en omdat dat exemplaar wordt gedeeld tussen alle threads in de webtoepassing, fungeert deze klassevariabele als een cache. De BuildSiteMap methode maakt ook gebruik van de gegevenscache, maar alleen als een manier om een melding te ontvangen wanneer de onderliggende databasegegevens in de Categories of Products tabellen worden gewijzigd. Houd er rekening mee dat de waarde die in de gegevenscache wordt geplaatst, alleen de huidige datum en tijd is. De werkelijke siteoverzichtgegevens worden niet in de gegevenscache geplaatst.

De BuildSiteMap methode rondt af door het rootknooppunt van de sitemap te retourneren.

De resterende methoden zijn redelijk eenvoudig. GetRootNodeCore is verantwoordelijk voor het retourneren van het hoofdknooppunt. Omdat BuildSiteMap de root retourneert, geeft GetRootNodeCore simpelweg de retourwaarde van BuildSiteMap terug. De methode OnSiteMapChanged stelt root weer in op null wanneer het cache-item wordt verwijderd. Met de root teruggezet naar null, wordt bij de volgende keer dat BuildSiteMap wordt aangeroepen de structuur van de sitekaart opnieuw opgebouwd. Ten slotte retourneert de CachedDate eigenschap de datum- en tijdwaarde die is opgeslagen in de gegevenscache, als er een dergelijke waarde bestaat. Deze eigenschap kan worden gebruikt door een paginaontwikkelaar om te bepalen wanneer de siteoverzichtsgegevens voor het laatst in de cache zijn opgeslagen.

Stap 7: Registratie van deNorthwindSiteMapProvider

Om ervoor te zorgen dat onze webtoepassing de NorthwindSiteMapProvider siteoverzichtprovider gebruikt die in stap 6 is gemaakt, moeten we deze registreren in de <siteMap> sectie van Web.config. Voeg met name de volgende markeringen toe binnen het <system.web> element in Web.config:

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

Deze markering doet twee dingen: ten eerste geeft het aan dat de ingebouwde AspNetXmlSiteMapProvider de standaardprovider voor siteoverzicht is. Ten tweede registreert het de aangepaste siteoverzichtprovider die in stap 6 is gemaakt met de gebruiksvriendelijke naam Northwind.

Opmerking

Voor siteoverzichtproviders die zich in de map App_Code van de toepassing bevinden, is de waarde van het type kenmerk gewoon de klassenaam. U kunt ook de aangepaste sitemapprovider in een afzonderlijk Class Library-project hebben gemaakt, waarbij de gecompileerde assembly in de /Bin-map van de webtoepassing wordt geplaatst. In dat geval is de type kenmerkwaarde Naamruimte.ClassName, AssemblyName .

Neem na het bijwerken Web.config de tijd om een pagina van de handleidingen in een browser te bekijken. Houd er rekening mee dat in de navigatie-interface aan de linkerkant nog steeds de secties en zelfstudies worden weergegeven die zijn gedefinieerd in Web.sitemap. Dit komt omdat we de standaardprovider hebben verlaten AspNetXmlSiteMapProvider . Als u een navigatie-interface van de gebruiker wilt maken die gebruikmaakt van het NorthwindSiteMapProvider, moet u expliciet aangeven dat de Northwind-sitemapprovider moet worden gebruikt. In stap 8 ziet u hoe u dit kunt doen.

Stap 8: Siteoverzichtinformatie weergeven met behulp van de aangepaste siteoverzichtprovider

Nu de aangepaste siteoverzichtprovider is gemaakt en geregistreerd in Web.config, kunnen we beginnen met het toevoegen van navigatiebesturingselementen aan de pagina's Default.aspx, ProductsByCategory.aspx en ProductDetails.aspx in de SiteMapProvider map. Begin met het openen van de Default.aspx pagina en sleep een SiteMapPath vanuit de toolbox naar de Designer. Het besturingselement SiteMapPath bevindt zich in de sectie Navigatie van de werkset.

Een SiteMapPath toevoegen aan Default.aspx

Afbeelding 16: Een SiteMapPath toevoegen aan Default.aspx (klik om de afbeelding op volledige grootte weer te geven)

Het besturingselement SiteMapPath geeft een breadcrumb weer, waarmee de locatie van de huidige pagina in het siteoverzicht wordt aangegeven. We hebben bovenaan de basispagina een SiteMapPath toegevoegd in de zelfstudie Basispagina's en Sitenavigatie .

Neem even de tijd om deze pagina via een browser weer te geven. De SiteMapPath die in afbeelding 16 is toegevoegd, maakt gebruik van de standaardsiteoverzichtprovider en haalt de bijbehorende gegevens op Web.sitemap. Daarom toont de breadcrumb Home > Customizing the Site Map, net zoals de breadcrumb in de rechterbovenhoek.

Breadcrumb maakt gebruik van de standaardsiteoverzichtprovider

Afbeelding 17: Breadcrumb maakt gebruik van de standaardsiteoverzichtprovider (klik om de afbeelding op volledige grootte weer te geven)

Als u het SiteMapPath wilt toevoegen in afbeelding 16, gebruikt u de aangepaste siteoverzichtprovider die we in stap 6 hebben gemaakt. Stel de eigenschap SiteMapProvider in op Northwind, de naam die we hebben toegewezen aan de NorthwindSiteMapProviderWeb.config. Helaas blijft de ontwerper de standaardprovider voor siteoverzichten gebruiken, maar als u de pagina bezoekt via een browser nadat u deze eigenschap hebt gewijzigd, ziet u dat de breadcrumb nu gebruikmaakt van de aangepaste siteoverzichtprovider.

Schermopname die laat zien hoe de breadcrumb de aangepaste siteoverzichtprovider weergeeft.

Afbeelding 18: De Breadcrumb maakt nu gebruik van de aangepaste siteoverzichtprovider NorthwindSiteMapProvider (klik om de afbeelding op volledige grootte weer te geven)

Het besturingselement SiteMapPath geeft een functionelere gebruikersinterface weer in de ProductsByCategory.aspx en ProductDetails.aspx pagina's. Voeg een SiteMapPath toe aan deze pagina's en stel de SiteMapProvider eigenschap in beide in op Northwind. Klik Default.aspx op de koppeling Producten weergeven voor Dranken en klik vervolgens op de koppeling Details weergeven voor Chai Tea. Zoals in afbeelding 19 wordt weergegeven, bevat de breadcrumb de huidige sectie van de sitekaart (Chai Tea) en de bovenliggende secties: Dranken en Alle categorieën.

Schermopname die laat zien hoe de breadcrumb de huidige sectie van de sitemap (Chai Tea) en de bovenliggende categorieën (Dranken en Alle Categorieën) weergeeft.

Afbeelding 19: De Breadcrumb maakt nu gebruik van de aangepaste siteoverzichtprovider NorthwindSiteMapProvider (klik om de volledige afbeelding weer te geven)

Andere elementen van de gebruikersinterface voor navigatie kunnen naast sitemappath worden gebruikt, zoals de besturingselementen Menu en TreeView. De Default.aspx, ProductsByCategory.aspxen ProductDetails.aspx pagina's in de download voor deze zelfstudie bevatten bijvoorbeeld allemaal menubesturingselementen (zie afbeelding 20). Zie de geavanceerde sitenavigatiefuncties van ASP.NET 2.0 en het gedeelte Sitenavigatiebesturingselementen gebruiken van de quickstarts ASP.NET 2.0 voor een uitgebreider overzicht van de navigatiebesturingselementen en het siteoverzichtsysteem in ASP.NET 2.0.

Het menu-besturingselement bevat elk van de categorieën en producten

Afbeelding 20: Het menubesturingselement bevat elk van de categorieën en producten (klik om de afbeelding op volledige grootte weer te geven)

Zoals eerder in deze zelfstudie is vermeld, kan de structuur van de siteoverzicht programmatisch worden geopend via de SiteMap klasse. De volgende code retourneert de root SiteMapNode van de standaardprovider.

SiteMapNode root = SiteMap.RootNode;

Omdat de AspNetXmlSiteMapProvider standaardprovider voor onze toepassing is, retourneert de bovenstaande code het hoofdknooppunt dat is gedefinieerd in Web.sitemap. Als u wilt verwijzen naar een andere sitemapprovider dan de standaard, gebruikt u de SiteMap klasse-eigenschap zoals Providers.

SiteMapNode root = SiteMap.Providers["name"].RootNode;

Waar naam de naam is van de aangepaste sitemapprovider (Northwind, voor onze webtoepassing).

Gebruik SiteMap.Providers["name"] om toegang te krijgen tot een lid dat specifiek is voor een siteoverzichtsprovider, haal het providerexemplaar op en cast het vervolgens naar het juiste type. Als u bijvoorbeeld de NorthwindSiteMapProvider eigenschap s CachedDate wilt weergeven op een ASP.NET pagina, gebruikt u de volgende code:

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!";
}

Opmerking

Zorg ervoor dat u de functie sql-cacheafhankelijkheid test. Nadat u de Default.aspx, ProductsByCategory.aspx, en ProductDetails.aspx pagina's hebt bezocht, gaat u naar een van de handleidingen in de sectie Bewerken, Invoegen en Verwijderen en bewerkt u de naam van een categorie of product. Ga vervolgens terug naar een van de pagina's in de SiteMapProvider map. Ervan uitgaande dat er voldoende tijd is verstreken voor het pollingmechanisme om de wijziging in de onderliggende database te noteren, moet het siteoverzicht worden bijgewerkt om de nieuwe product- of categorienaam weer te geven.

Samenvatting

ASP.NET siteoverzichtfuncties van 2.0 bevatten een SiteMap klasse, een aantal ingebouwde navigatiewebbesturingselementen en een standaardsiteoverzichtprovider waarin wordt verwacht dat de siteoverzichtinformatie behouden blijft in een XML-bestand. Als u siteoverzichtgegevens wilt gebruiken van een andere bron, zoals van een database, de architectuur van de toepassing of een externe webservice, moeten we een aangepaste siteoverzichtprovider maken. Dit omvat het maken van een klasse die rechtstreeks of indirect is afgeleid van de SiteMapProvider klasse.

In deze handleiding hebben we gezien hoe u een aangepaste siteprovider maakt die het siteoverzicht baseert op de product- en categoriegegevens die zijn verzameld uit de applicatiearchitectuur. Onze provider heeft de StaticSiteMapProvider klasse uitgebreid en omvat het maken van een BuildSiteMap methode die de gegevens heeft opgehaald, de siteoverzichthiërarchie heeft samengesteld en de resulterende structuur in de cache heeft opgeslagen in een variabele op klasseniveau. We hebben een SQL-cacheafhankelijkheid met een callback-functie gebruikt om de structuur in de cache ongeldig te maken wanneer de onderliggende Categories of Products gegevens worden gewijzigd.

Veel plezier met programmeren!

Meer lezen

Raadpleeg de volgende bronnen voor meer informatie over de onderwerpen die in deze zelfstudie worden besproken:

Over de auteur

Scott Mitchell, auteur van zeven ASP/ASP.NET-boeken en oprichter van 4GuysFromRolla.com, werkt sinds 1998 met Microsoft-webtechnologieën. Scott werkt als onafhankelijk consultant, trainer en schrijver. Zijn laatste boek is Sams Teach Yourself ASP.NET 2.0 in 24 uur. Hij kan worden bereikt op mitchell@4GuysFromRolla.com.

Speciale dank aan

Deze tutorialreeks is beoordeeld door veel behulpzame beoordelers. Hoofdrevisoren voor deze handleiding waren Dave Gardner, Zack Jones, Teresa Murphy, en Bernadette Leigh. Bent u geïnteresseerd in het bekijken van mijn aanstaande MSDN-artikelen? Zo ja, laat iets van je horen via mitchell@4GuysFromRolla.com.