Tworzenie niestandardowego dostawcy map witryn opartych na bazie danych (C#)

przez Scott Mitchell

Pobierz kod lub Pobierz plik PDF

Domyślny dostawca mapy witryny w programie ASP.NET 2,0 pobiera dane z statycznego pliku XML. Chociaż dostawca oparty na języku XML jest odpowiedni dla wielu małych i średnich witryn sieci Web, większe aplikacje sieci Web wymagają szerszej mapy witryn. W tym samouczku utworzymy niestandardowego dostawcę mapy witryny, który pobiera dane z warstwy logiki biznesowej, która z kolei pobiera dane z bazy danych.

Wprowadzenie

ASP.NET 2,0 s funkcja mapy witryny umożliwia deweloperom stron Definiowanie mapy witryny aplikacji sieci Web w pewnym trwałym nośniku, na przykład w pliku XML. Po zdefiniowaniu dane mapy lokacji można uzyskać programowo przy użyciu klasySiteMap w przestrzeni nazwSystem.Web lub za pomocą różnych kontrolek sieci Web nawigacji, takich jak ścieżki mapy witryny, menu i drzewa TreeView. System mapy lokacji korzysta z modelu dostawcy , aby można było utworzyć różne implementacje serializacji mapy witryny i podłączyć je do aplikacji sieci Web. Domyślny dostawca mapy witryny dostarczany z ASP.NET 2,0 utrzymuje strukturę mapy witryny w pliku XML. Z powrotem na stronach wzorcowych i w samouczku nawigowania po witrynie został utworzony plik o nazwie Web.sitemap, który zawierał tę strukturę i który ZAKTUALIZOWAŁ kod XML za pomocą każdej nowej sekcji samouczka.

Domyślny dostawca mapy witryny oparty na języku XML działa prawidłowo, jeśli struktura mapy witryny jest dość statyczna, na przykład w przypadku tych samouczków. Jednak w wielu scenariuszach jest wymagana bardziej dynamiczna mapa witryny. Rozważmy mapę witryny pokazaną na rysunku 1, gdzie każda kategoria i produkt są wyświetlane jako sekcje w strukturze witryny sieci Web. W przypadku tej mapy witryny odwiedzanie strony sieci Web odpowiadającej węzłowi głównemu może spowodować wyświetlenie wszystkich kategorii, a podczas odwiedzania określonej strony sieci Web w kategorii należy wyświetlić listę produktów kategorii i wyświetlić określoną stronę produktu w sieci Web.

kategorie i produkty korzeń strukturę mapy witryny

Rysunek 1. kategorie i produkty korzeń strukturę mapy witryny (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Chociaż ta struktura kategorii i produktu może być zakodowana w pliku Web.sitemap, należy ją zaktualizować za każdym razem, gdy Kategoria lub produkt został dodany, usunięty lub zmieniono jego nazwę. W związku z tym, konserwacja mapy witryny będzie znacznie uproszczona, jeśli struktura została pobrana z bazy danych lub, najlepiej, z warstwy logiki biznesowej architektury aplikacji. Dzięki temu, jak produkty i kategorie zostały dodane, zmieniono nazwę lub usunięto, mapa witryny zostanie automatycznie zaktualizowana w celu odzwierciedlenia tych zmian.

Ponieważ ASP.NET 2,0 s Serializacja mapy witryny jest zbudowana korzystającego modelu dostawcy, możemy utworzyć własnego niestandardowego dostawcę mapy witryny, który będzie zbierał dane z alternatywnego magazynu danych, takiego jak baza danych lub architektura. W tym samouczku utworzysz dostawcę niestandardowego, który pobiera dane z LOGIKI biznesowej. Zacznij korzystać z aplikacji.

Note

Niestandardowy dostawca mapy witryny utworzony w tym samouczku jest ściśle połączony z architekturą i modelem danych aplikacji. Marcin Prosise s przechowujący mapy witryn w SQL Server i dostawca mapy witryny SQL zaczekał na artykuły badanie uogólnionego podejścia do przechowywania danych mapy lokacji w SQL Server.

Krok 1. Tworzenie niestandardowych stron sieci Web dostawcy mapy witryny

Przed rozpoczęciem tworzenia niestandardowego dostawcy mapy witryny należy najpierw dodać strony ASP.NET, które będą potrzebne w tym samouczku. Zacznij od dodania nowego folderu o nazwie SiteMapProvider. Następnie Dodaj następujące strony ASP.NET do tego folderu, aby upewnić się, że każda strona jest skojarzona z Site.master stroną wzorcową:

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

Dodaj także podfolder CustomProviders do folderu App_Code.

Dodaj strony ASP.NET dla samouczków związanych z dostawcą mapy witryny

Rysunek 2. dodawanie stron ASP.NET dla samouczków związanych z dostawcą mapy witryny

Ponieważ dla tej sekcji istnieje tylko jeden samouczek, nie potrzebujemy Default.aspx, aby wyświetlić listę samouczków sekcji. Zamiast tego, Default.aspx będą wyświetlać kategorie w kontrolce GridView. Zajmiemy się tym w kroku 2.

Następnie zaktualizuj Web.sitemap, aby dołączyć odwołanie do strony Default.aspx. Należy dodać następujące znaczniki po <siteMapNode>buforowania:

<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." />

Po aktualizacji Web.sitemapzapoznaj się z witryną internetową samouczków za pomocą przeglądarki. Menu po lewej stronie zawiera teraz element samouczka dla jedynego dostawcy mapy witryny.

Mapa witryny zawiera teraz wpis do samouczka dotyczącego dostawcy mapy witryny

Rysunek 3. Mapa witryny zawiera teraz wpis do samouczka dotyczącego dostawcy mapy witryny

Ten samouczek przedstawia tworzenie niestandardowego dostawcy mapy witryny i Konfigurowanie aplikacji sieci Web do korzystania z tego dostawcy. W szczególności utworzymy dostawcę, który zwróci mapę witryny, która zawiera węzeł główny wraz z węzłem dla każdej kategorii i produktu, jak przedstawiono na rysunku 1. Ogólnie rzecz biorąc, każdy węzeł w mapie witryny może określać adres URL. W przypadku naszej mapy witryny główny adres URL węzła zostanie ~/SiteMapProvider/Default.aspx, co spowoduje wyświetlenie listy wszystkich kategorii w bazie danych. Każdy węzeł kategorii na mapie witryny będzie miał adres URL wskazujący na ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID, który będzie zawierać listę wszystkich produktów w określonym IDkategorii. Na koniec każdy węzeł mapy witryny produktu wskaże ~/SiteMapProvider/ProductDetails.aspx?ProductID=productID, co spowoduje wyświetlenie szczegółowych informacji o produkcie.

Aby rozpocząć, należy utworzyć strony Default.aspx, ProductsByCategory.aspxi ProductDetails.aspx. Te strony są wykonywane odpowiednio w krokach 2, 3 i 4. Ponieważ nacisk tego samouczka znajduje się w dostawcach mapy witryny, a ponieważ przeszłe samouczki pomogły utworzyć różne raporty wzorzec/szczegóły wielostronicowego, będziemy pospiesz przez kroki od 2 do 4. Jeśli potrzebujesz odświeżacza przy tworzeniu raportów master/detail obejmujących wiele stron, zapoznaj się z artykułem dotyczącym filtrowania wzorca/szczegółów na dwóch stronach .

Krok 2. Wyświetlanie listy kategorii

Otwórz stronę Default.aspx w folderze SiteMapProvider i przeciągnij widok GridView z przybornika do projektanta, ustawiając jego ID na Categories. Z taga inteligentnego w widoku GridView powiąż go z nowym elementem ObjectDataSource o nazwie CategoriesDataSource i skonfiguruj go tak, aby pobierał dane przy użyciu metody CategoriesBLL klasy s GetCategories. Ponieważ w tym widoku GridView są wyświetlane kategorie i nie są dostępne funkcje modyfikacji danych, Ustaw listę rozwijaną na kartach UPDATE, INSERT i DELETE na wartość (brak).

skonfigurować element ObjectDataSource do zwracania kategorii za pomocą metody GetCategories

Ilustracja 4. Konfigurowanie elementu ObjectDataSource do zwracania kategorii za pomocą metody GetCategories (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

ustawić listy rozwijane na kartach Aktualizuj, Wstaw i Usuń do (brak).

Rysunek 5. Ustawianie list rozwijanych w kartach Aktualizuj, Wstaw i Usuń do (brak) (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Po zakończeniu działania Kreatora konfiguracji źródła danych program Visual Studio doda BoundField do CategoryID, CategoryName, Description, NumberOfProductsi BrochurePath. Edytuj widok GridView, aby zawierał tylko CategoryName i Description BoundFields i zaktualizować Właściwość CategoryName BoundField s HeaderText do kategorii.

Następnie Dodaj element HyperLinkField i umieść go tak, aby pozostały do lewej strony. Ustaw właściwość DataNavigateUrlFields na CategoryID i Właściwość DataNavigateUrlFormatString na ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}. Ustaw właściwość Text, aby wyświetlić produkty.

Dodaj element HyperLinkField do kategorii GridView

Ilustracja 6. Dodawanie HyperLinkField do Categories GridView

Po utworzeniu elementu ObjectDataSource i dostosowaniu pól GridView, dwa formanty deklaratywne znaczniki będą wyglądać następująco:

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

Rysunek 7 przedstawia Default.aspx, gdy jest wyświetlany za pomocą przeglądarki. Kliknięcie linku Pokaż produkty kategorii spowoduje przejście do ProductsByCategory.aspx?CategoryID=categoryID, który zostanie skompilowany w kroku 3.

każda kategoria jest wyświetlana razem z linkiem Wyświetl produkty

Rysunek 7. Każda kategoria jest wyświetlana wraz z linkiem Wyświetl produkty (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Krok 3. Wyświetlanie listy wybranych produktów kategorii s

Otwórz stronę ProductsByCategory.aspx i Dodaj widok GridView, nadając jej nazwę ProductsByCategory. Ze znacznika inteligentnego Powiąż widok GridView z nowym elementem ObjectDataSource o nazwie ProductsByCategoryDataSource. Skonfiguruj element ObjectDataSource do używania metody GetProductsByCategoryID(categoryID) ProductsBLL klasy s i Ustaw listę rozwijaną na wartość (brak) na kartach Aktualizuj, Wstaw i Usuń.

użyć metody ProductsBLL Class s GetProductsByCategoryID (IDKategorii)

Ilustracja 8. użyj metody GetProductsByCategoryID(categoryID) ProductsBLL Class (kliknij,Aby wyświetlić obraz o pełnym rozmiarze)

Ostatnim krokiem w Kreatorze konfiguracji źródła danych jest wyświetlanie danych źródłowych parametrów dla IDkategorii. Ponieważ te informacje są przesyłane za pośrednictwem pola QueryString CategoryID, z listy rozwijanej wybierz pozycję QueryString i wprowadź wartość IDKategorii w polu tekstowym QueryStringField, jak pokazano na rysunku 9. Kliknij przycisk Zakończ, aby zakończyć pracę kreatora.

użyć pola IDKategorii QueryString dla parametru IDKategorii

Rysunek 9. użyj pola CategoryID QueryString dla parametru IDkategorii (kliknij,Aby wyświetlić obraz o pełnym rozmiarze)

Po ukończeniu działania kreatora program Visual Studio doda odpowiednie BoundFields oraz CheckBoxField do widoku GridView dla pól danych produktu. Usuń wszystkie oprócz ProductName, UnitPricei SupplierName BoundFields. Dostosuj te trzy BoundFields HeaderText właściwości, aby odczytywać odpowiednio produkt, cenę i dostawcę. Sformatuj UnitPrice BoundField jako walutę.

Następnie Dodaj element HyperLinkField i przenieś go do pozycji z lewej strony. Ustaw jej Właściwość Text, aby wyświetlić szczegóły, jej Właściwość DataNavigateUrlFields do ProductIDoraz jej Właściwość DataNavigateUrlFormatString do ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}.

Dodaj szczegóły widoku HyperLinkField wskazujące ProductDetails. aspx

Ilustracja 10. Dodawanie szczegółów widoku HyperLinkField, które wskazują ProductDetails.aspx

Po wprowadzeniu tych dostosowań, znaczniki deklaratywne GridView i ObjectDataSource s powinny wyglądać podobnie do następujących:

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

Wróć do wyświetlania Default.aspx za pomocą przeglądarki i kliknij link Wyświetl produkty dla napojów. Spowoduje to przejście do ProductsByCategory.aspx?CategoryID=1, wyświetlenie nazw, cen i dostawców produktów w bazie danych Northwind należących do kategorii napoje (patrz rysunek 11). Możesz bardziej ulepszyć Tę stronę, aby dołączyć łącze do strony z listą kategorii (Default.aspx) i formantem DetailsView lub FormView, który wyświetla wybraną nazwę kategorii i opis.

są wyświetlane nazwy napojów, ceny i dostawcy

Rysunek 11. wyświetlane są nazwy napojów, ceny i dostawcy (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Krok 4. Pokazywanie szczegółowych informacji o produkcie

Na ostatniej stronie ProductDetails.aspxwyświetlane są szczegóły wybranych produktów. Otwórz ProductDetails.aspx i przeciągnij element DetailsView z przybornika do projektanta. Ustaw właściwość DetailsView s ID, aby ProductInfo i wyczyścić Height i Width wartości właściwości. Z jego tagu inteligentnego Powiąż element DetailsView z nowym elementem ObjectDataSource o nazwie ProductDataSource, konfigurując element ObjectDataSource do ściągania danych z metody GetProductByProductID(productID) klasy ProductsBLL. Podobnie jak w przypadku wcześniejszych stron sieci Web utworzonych w krokach 2 i 3, Ustaw listę rozwijaną na kartach Aktualizuj, Wstaw i Usuń do (brak).

skonfigurować element ObjectDataSource do korzystania z metody GetProductByProductID (productID)

Ilustracja 12. Konfigurowanie elementu ObjectDataSource do używania metody GetProductByProductID(productID) (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Ostatnim krokiem w Kreatorze konfiguracji źródła danych jest wyświetlanie danych źródłowych parametru ProductID . Ponieważ te dane pochodzą z pola QueryString ProductID, Ustaw listę rozwijaną na QueryString i pole tekstowe QueryStringField na ProductID. Na koniec kliknij przycisk Zakończ, aby zakończyć pracę kreatora.

skonfigurować parametr productID, aby ściągnąć jego wartość z pola IDProduktu QueryString

Ilustracja 13. Skonfiguruj parametr ProductID , aby ściągnąć jego wartość z pola ProductID QueryString (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Po zakończeniu działania Kreatora konfiguracji źródła danych program Visual Studio utworzy odpowiednie BoundFields i CheckBoxField w widoku DetailsView dla pól danych produktu. Usuń ProductID, SupplierIDi CategoryID BoundFields i skonfiguruj pozostałe pola zgodnie z oczekiwaniami. Po kilkuniu konfiguracji estetycznych, moje znaczniki deklaratywne i ObjectDataSource języka, wyglądały następująco:

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

Aby przetestować Tę stronę, Wróć do Default.aspx i kliknij pozycję Wyświetl produkty dla kategorii napoje. Z listy napojów produktów kliknij link Wyświetl szczegóły dla Chai herbata. Spowoduje to przejście do ProductDetails.aspx?ProductID=1, w którym są wyświetlane szczegółowe informacje o Chai Tea (zobacz rysunek 14).

Chai herbata, Kategoria, Cena i inne informacje są wyświetlane

Rysunek 14: dostawca Chai herbata, Kategoria, Cena i inne informacje są wyświetlane (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Krok 5. zrozumienie wewnętrznego działania dostawcy mapy witryny

Mapa witryny jest reprezentowana w pamięci serwera sieci Web jako kolekcja wystąpień SiteMapNode, które tworzą hierarchię. Musi istnieć tylko jeden katalog główny, a wszystkie węzły inne niż główne muszą mieć dokładnie jeden węzeł nadrzędny, a wszystkie węzły mogą mieć dowolną liczbę elementów podrzędnych. Każdy obiekt SiteMapNode reprezentuje sekcję w strukturze witryny sieci Web; te sekcje często mają odpowiednią stronę sieci Web. W związku z tym klasaSiteMapNode ma właściwości, takie jak Title, Urli Description, które zawierają informacje dotyczące sekcji, którą reprezentuje SiteMapNode. Istnieje również właściwość Key, która jednoznacznie identyfikuje poszczególne SiteMapNode w hierarchii, a także właściwości służące do ustanowienia tej hierarchii ChildNodes, ParentNode, NextSibling, PreviousSiblingi tak dalej.

Rysunek 15 przedstawia ogólną strukturę mapy witryny na rysunku 1, ale z szczegółowymi informacjami o implementacji.

każda SiteMapNode ma właściwości, takie jak tytuł, adres URL, klucz i tak dalej.

Ilustracja 15: Każda SiteMapNode ma właściwości, takie jak Title, Url, Keyi tak dalej (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Mapa witryny jest dostępna za pomocą klasySiteMap w przestrzeni nazwSystem.Web. Ta właściwość klasy s RootNode zwraca wystąpienie SiteMapNode głównej mapy witryny. CurrentNode zwraca SiteMapNode, którego właściwość Url pasuje do adresu URL aktualnie żądanej strony. Ta klasa jest używana wewnętrznie przez kontrolki sieci Web ASP.NET 2,0 s nawigacji.

Po uzyskaniu dostępu do właściwości SiteMap klasy s musi ona serializować strukturę mapy witryny z pewnego trwałego nośnika do pamięci. Jednak logika serializacji mapy witryny nie jest sztywno zakodowana w klasie SiteMap. Zamiast tego w czasie wykonywania Klasa SiteMap określa, który dostawca mapy lokacji ma być używany do serializacji. Domyślnie używana jest klasaXmlSiteMapProvider , która odczytuje strukturę mapy lokacji z poprawnie sformatowanego pliku XML. Jednak w przypadku małej ilości pracy możemy utworzyć własnego niestandardowego dostawcę mapy witryny.

Wszyscy dostawcy mapy witryny muszą pochodzić z klasySiteMapProvider, która zawiera podstawowe metody i właściwości wymagane przez dostawców mapy witryny, ale pomija wiele szczegółów implementacji. Druga klasa, StaticSiteMapProvider, rozszerza klasę SiteMapProvider i zawiera bardziej niezawodną implementację wymaganej funkcjonalności. Wewnętrznie w StaticSiteMapProvider są przechowywane SiteMapNode wystąpienia mapy witryny w Hashtable i przedstawiono metody, takie jak AddNode(child, parent), RemoveNode(siteMapNode), i Clear(), które dodają i usuwają SiteMapNode s do wewnętrznej Hashtable. XmlSiteMapProvider pochodzi od StaticSiteMapProvider.

Podczas tworzenia niestandardowego dostawcy mapy witryny, który rozszerza StaticSiteMapProvider, istnieją dwie metody abstrakcyjne, które muszą zostać zastąpione: BuildSiteMap i GetRootNodeCore. BuildSiteMap, zgodnie z jego nazwą, jest odpowiedzialny za ładowanie struktury mapy witryny z magazynu trwałego i konstruowanie jej w pamięci. GetRootNodeCore zwraca węzeł główny w mapie witryny.

Zanim aplikacja sieci Web będzie mogła korzystać z dostawcy mapy witryny, musi być zarejestrowana w konfiguracji aplikacji. Domyślnie Klasa XmlSiteMapProvider jest zarejestrowana przy użyciu nazwy AspNetXmlSiteMapProvider. Aby zarejestrować dodatkowych dostawców mapy witryny, Dodaj następujące znaczniki do Web.config:

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

Wartość Nazwa przypisuje do dostawcy nazwę, którą można odczytać przez człowieka, podczas gdy Typ określa w pełni kwalifikowaną nazwę typu dostawcy mapy witryny. Poszukamy konkretnych wartości nazwy i wartości typów w kroku 7, po utworzeniu niestandardowego dostawcy mapy witryny.

Klasa dostawcy mapy witryny jest tworzona przy pierwszym dostępie z klasy SiteMap i pozostaje w pamięci przez okres istnienia aplikacji sieci Web. Ponieważ istnieje tylko jedno wystąpienie dostawcy mapy witryny, które może zostać wywołane przez wielu osób odwiedzających witrynę sieci Web, konieczna jest bezpieczna wątkowoMetoda dostawcy.

Ze względu na wydajność i skalowalność należy pamiętać, że pamięć podręczna jest w pamięci podręcznej struktury mapy lokacji i zwrócić tę buforowaną strukturę zamiast tworzyć ją przy każdym wywołaniu metody BuildSiteMap. BuildSiteMap może być wywoływana kilka razy na żądanie strony dla użytkownika, w zależności od kontrolek nawigacyjnych używanych na stronie oraz głębokości struktury mapy witryny. W każdym przypadku, jeśli nie buforuje struktury mapy witryny w BuildSiteMap następnie przy każdym wywołaniu, należy ponownie pobrać informacje o produkcie i kategorii z architektury (co spowoduje wykonanie zapytania do bazy danych). Zgodnie z opisem w poprzednich samouczkach buforowania dane buforowane mogą stać się przestarzałe. Aby to wymusić, możemy użyć wygaśnięcia opartych na zależnościach czasu lub w pamięci podręcznej SQL.

Note

Dostawca mapy witryny może opcjonalnie zastąpić metodęInitialize. Initialize jest wywoływany podczas pierwszego wystąpienia dostawcy mapy witryny i jest on przeszukiwany wszystkie atrybuty niestandardowe przypisane do dostawcy w Web.config w <add> elementu, takie jak: <add name="name" type="type" customAttribute="value" />. Jest to przydatne, jeśli chcesz zezwolić deweloperowi na określanie różnych ustawień związanych z dostawcą mapy witryny, bez konieczności modyfikowania kodu dostawcy. Na przykład w przypadku odczytywania danych kategorii i produktów bezpośrednio z bazy danych, w przeciwieństwie do architektury, firma Microsoft chce pozwolić, aby deweloper strony określił parametry połączenia bazy danych za pośrednictwem Web.config zamiast korzystać z zakodowanej wartości w kodzie dostawcy. Niestandardowy dostawca mapy witryny zostanie skompilowany w kroku 6 nie przesłania tej metody Initialize. Przykład użycia metody Initialize można znaleźć w artykule Jan Prosise s przechowującym mapy witryn w programie SQL Server .

Krok 6. Tworzenie niestandardowego dostawcy mapy witryny

Aby utworzyć niestandardowego dostawcę mapy witryny, który kompiluje mapę witryny z kategorii i produktów w bazie danych Northwind, musimy utworzyć klasę rozszerzającą StaticSiteMapProvider. W kroku 1 zażądano dodania folderu CustomProviders w folderze App_Code — Dodaj nową klasę do tego folderu o nazwie NorthwindSiteMapProvider. Dodaj następujący kod do klasy 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?;
        }
    }
}

Niech Rozpocznij od eksplorowania tej klasy BuildSiteMap Metoda, która rozpoczyna się od instrukcjilock. Instrukcja lock zezwala tylko jednemu wątkowi na wprowadzenie, a tym samym serializacji dostęp do jego kodu i uniemożliwia dwa współbieżne wątki przed przechodzeniem na jeden inny wyglądają.

Zmienna SiteMapNode na poziomie klasy root służy do buforowania struktury mapy witryny. Gdy mapa witryny jest tworzona po raz pierwszy lub po raz pierwszy po zmodyfikowaniu danych źródłowych, root zostanie null, a struktura mapy witryny zostanie skonstruowana. Węzeł główny mapy witryny jest przypisywany do root podczas procesu konstruowania, dzięki czemu przy następnym wywołaniu tej metody root nie będzie null. W związku z tym, tak długo, jak root nie jest null struktura mapy witryny zostanie zwrócona do obiektu wywołującego bez konieczności ponownego tworzenia.

Jeśli katalog główny jest null, struktura mapy witryny jest tworzona na podstawie informacji o produkcie i kategorii. Mapę witryny można utworzyć, tworząc SiteMapNode wystąpienia, a następnie tworząc hierarchię za pomocą wywołań do metody StaticSiteMapProvider klasy s AddNode. AddNode wykonuje wewnętrzne Księgowość, przechowując wystąpienia SiteMapNode w Hashtable. Przed rozpoczęciem konstruowania hierarchii rozpoczynamy od wywołania metody Clear, która czyści elementy z Hashtablewewnętrznej. Następnie Metoda ProductsBLL klasy s GetProducts i ProductsDataTable wyniki są przechowywane w zmiennych lokalnych.

Konstrukcja mapy witryny rozpocznie się, tworząc węzeł główny i przypisując go do root. Przeciążanie konstruktoraSiteMapNode s użyte w tym miejscu i w tym BuildSiteMap jest przenoszone następujące informacje:

  • Odwołanie do dostawcy mapy witryny (this).
  • Key``SiteMapNode. Ta wymagana wartość musi być unikatowa dla każdego SiteMapNode.
  • Url``SiteMapNode. Url jest opcjonalne, ale jeśli zostały podane, każda SiteMapNode Url wartość musi być unikatowa.
  • Wymagany Title``SiteMapNode s.

Wywołanie metody AddNode(root) dodaje root SiteMapNode do mapy witryny jako element główny. Następnie zostaną wyliczone wszystkie ProductRow w ProductsDataTable. Jeśli istnieje już SiteMapNode dla kategorii Current Product, odwołanie jest przywoływane. W przeciwnym razie nowy SiteMapNode kategorii zostanie utworzony i dodany jako element podrzędny SiteMapNode``root za pomocą wywołania metody AddNode(categoryNode, root). Po znalezieniu lub utworzeniu odpowiedniej kategorii SiteMapNode węźle zostanie utworzony SiteMapNode dla bieżącego produktu i dodany jako element podrzędny kategorii SiteMapNode za pośrednictwem AddNode(productNode, categoryNode). Należy zauważyć, że Kategoria SiteMapNode s Url wartość właściwości jest ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID, podczas gdy produktu SiteMapNode s Url zostanie przypisany ~/SiteMapNode/ProductDetails.aspx?ProductID=productID.

Note

Te produkty, które mają NULL wartość bazy danych dla ich CategoryID są pogrupowane w kategorii SiteMapNode której Właściwość Title ma wartość None, a właściwość Url jest ustawiona na pusty ciąg. Zdecydowałem się ustawić Url na pusty ciąg, ponieważ metoda ProductBLL klasy GetProductsByCategory(categoryID) s nie ma obecnie możliwości zwrócenia tylko tych produktów z NULL CategoryID wartością. Warto również zademonstrować, jak kontrolki nawigacji renderują SiteMapNode, które nie mają wartości właściwości Url. Zachęcam do rozbudowania tego samouczka, tak aby Właściwość none SiteMapNode s Url wskazywała na ProductsByCategory.aspx, ale tylko te produkty mające NULL CategoryID wartości.

Po skonstruowaniu mapy witryny do pamięci podręcznej danych zostanie dodany dowolny obiekt, przy użyciu zależności pamięci podręcznej SQL Categories i Products tabelach za pośrednictwem obiektu AggregateCacheDependency. Wiemy, jak korzystać z zależności w pamięci podręcznej SQL w poprzednim samouczku, przy użyciu zależności pamięci podręcznej SQL. Dostawca niestandardowego mapowania witryn używa jednak przeciążenia metody Insertej pamięci podręcznej danych, która nie została jeszcze zaeksplorowania. To przeciążenie przyjmuje jako końcowy parametr wejściowy delegata, który jest wywoływany, gdy obiekt zostanie usunięty z pamięci podręcznej. W każdym przypadku przekazujemy nowe CacheItemRemovedCallback delegata wskazujące na metodę OnSiteMapChanged zdefiniowana w dalszej części klasy NorthwindSiteMapProvider.

Note

Reprezentacja mapy witryny w pamięci jest buforowana przez zmienną na poziomie klasy root. Ponieważ istnieje tylko jedno wystąpienie niestandardowej klasy dostawcy mapy witryny i ponieważ to wystąpienie jest współużytkowane przez wszystkie wątki w aplikacji sieci Web, ta zmienna klasy służy jako pamięć podręczna. Metoda BuildSiteMap używa również pamięci podręcznej danych, ale tylko jako środek do odbierania powiadomień, gdy dane źródłowe bazy danych w Categories lub tabelach Products zmienią się. Należy pamiętać, że wartość wprowadzona w pamięci podręcznej danych jest tylko bieżącą datą i godziną. Rzeczywiste dane mapy witryny nie są umieszczane w pamięci podręcznej danych.

Metoda BuildSiteMap zostanie zakończona, zwracając węzeł główny mapy witryny.

Pozostałe metody są dość proste. GetRootNodeCore jest odpowiedzialny za zwracanie węzła głównego. Ponieważ BuildSiteMap zwraca element główny, GetRootNodeCore po prostu zwraca wartość zwracaną BuildSiteMap s. Metoda OnSiteMapChanged ustawia root z powrotem na null po usunięciu elementu pamięci podręcznej. Po powrocie do nullprzy następnym czasie BuildSiteMap jest wywoływana, struktura mapy witryny zostanie odbudowana. Na koniec Właściwość CachedDate zwraca wartość daty i godziny przechowywanej w pamięci podręcznej danych, jeśli taka wartość istnieje. Ta właściwość może być używana przez dewelopera strony do określania, kiedy dane mapy lokacji były ostatnio buforowane.

Krok 7: rejestrowanieNorthwindSiteMapProvider

Aby nasza aplikacja sieci Web korzystała z dostawcy mapy witryny NorthwindSiteMapProvider utworzonego w kroku 6, należy zarejestrować go w sekcji <siteMap> w Web.config. W tym celu Dodaj następujące znaczniki w <system.web> elementu w Web.config:

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

Ten znacznik wykonuje dwie czynności: po pierwsze wskazuje, że wbudowana AspNetXmlSiteMapProvider jest domyślnym dostawcą mapy witryny; Po drugie rejestruje niestandardowego dostawcę mapy witryny utworzonego w kroku 6 z przyjazną dla człowieka nazwą Northwind.

Note

W przypadku dostawców mapy witryny znajdujących się w folderze App_Code aplikacji wartość atrybutu type jest po prostu nazwą klasy. Alternatywnie można utworzyć niestandardowego dostawcę mapy witryny w osobnym projekcie biblioteki klas z skompilowanym zestawem umieszczonym w katalogu aplikacji sieci Web /Bin. W takim przypadku type wartością atrybutu będzie przestrzeń nazw. ClassName, AssemblyName .

Po aktualizacji Web.configPoświęć chwilę na wyświetlenie dowolnej strony z samouczków w przeglądarce. Należy pamiętać, że interfejs nawigacji po lewej stronie nadal pokazuje sekcje i samouczki zdefiniowane w Web.sitemap. Jest to spowodowane tym, że AspNetXmlSiteMapProvider jako domyślny dostawca. Aby utworzyć element interfejsu użytkownika nawigacji, który używa NorthwindSiteMapProvider, należy jawnie określić, że powinien być używany dostawca mapy witryny Northwind. Zobaczmy, jak to zrobić w kroku 8.

Krok 8. Wyświetlanie informacji o mapie witryny przy użyciu niestandardowego dostawcy mapy witryny

Po utworzeniu niestandardowego dostawcy mapy witryny i zarejestrowaniu go w Web.configponownie przygotujemy do dodania kontrolek nawigacji do stron Default.aspx, ProductsByCategory.aspxi ProductDetails.aspx w folderze SiteMapProvider. Zacznij od otworzenia strony Default.aspx i przeciągnij SiteMapPath z przybornika na projektanta. Kontrolka ścieżki mapy witryny znajduje się w sekcji nawigacji w przyborniku.

dodać ścieżki mapy witryny do default. aspx

Ilustracja 16. Dodawanie ścieżki mapy witryny do Default.aspx (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Kontrolka ścieżki mapy witryny wyświetla pasek nawigacyjny wskazujący bieżącą lokalizację strony w ramach mapy witryny. Dodaliśmy ścieżki mapy witryny w górnej części strony wzorcowej z powrotem na stronach wzorcowych i w samouczku nawigacji po witrynie.

Poświęć chwilę na wyświetlenie tej strony za pomocą przeglądarki. Ścieżki mapy witryny dodany na rysunku 16 używa domyślnego dostawcy mapy witryny, pobierając jego dane z Web.sitemap. W związku z tym, w obszarze nawigacji znajdują się główne > dostosowywania mapy witryny, podobnie jak w przypadku stron nadrzędnych w prawym górnym rogu.

pasek nawigacyjny używa domyślnego dostawcy mapy witryny

Ilustracja 17. pasek nawigacyjny używa domyślnego dostawcy mapy witryny (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Aby dodać ścieżki mapy witryny (rysunek 16), Użyj niestandardowego dostawcy mapy witryny, który został utworzony w kroku 6, ustaw jego właściwośćSiteMapProvider na Northwind, nazwę, którą przypisano do NorthwindSiteMapProvider w Web.config. Niestety, Projektant nadal korzysta z domyślnego dostawcy mapy witryny, ale jeśli zostanie wyświetlona strona za pośrednictwem przeglądarki po wprowadzeniu tej zmiany właściwości, zobaczysz, że na tym pasku nawigacyjnym jest teraz używany niestandardowy dostawca mapy witryny.

do stron nadrzędnych jest teraz stosowana niestandardowa NorthwindSiteMapProvider dostawcy mapy witryny

Ilustracja 18. obraz przedstawiający teraz używa niestandardowego dostawcy mapy witryny NorthwindSiteMapProvider (kliknij, aby wyświetlić obrazy o pełnym rozmiarze)

Kontrolka ścieżki mapy witryny wyświetla bardziej funkcjonalny interfejs użytkownika na stronach ProductsByCategory.aspx i ProductDetails.aspx. Dodaj ścieżki mapy witryny do tych stron, ustawiając właściwość SiteMapProvider w obu wartościach na Northwind. W Default.aspx kliknij link Wyświetl produkty dla napojów, a następnie na linku Wyświetl szczegóły dla Chai herbata. Jak pokazano na rysunku 19, pasek nawigacyjny zawiera bieżącą sekcję mapy witryny (Chai herbata) i jej elementy nadrzędne: napoje i wszystkie kategorie.

do stron nadrzędnych jest teraz stosowana niestandardowa NorthwindSiteMapProvider dostawcy mapy witryny

Ilustracja 19. pasek nawigacyjny używa teraz niestandardowego dostawcy mapy witryny NorthwindSiteMapProvider (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Inne elementy interfejsu użytkownika nawigacji mogą służyć oprócz ścieżki mapy witryny, takich jak kontrolki menu i TreeView. Strony Default.aspx, ProductsByCategory.aspxi ProductDetails.aspx w pobraniu dla tego samouczka, na przykład wszystkie kontrolki menu dołączania (Zobacz Rysunek 20). Zobacz temat Badanie funkcji nawigacyjnych witryny ASP.NET 2,0 s i sekcji Używanie kontrolek nawigacji w witrynie przewodnika szybki start w programie ASP.NET 2,0 , aby uzyskać bardziej szczegółowy wygląd kontrolek nawigacji i systemu mapy witryny w ASP.NET 2,0.

formant menu zawiera listę kategorii i produktów

Ilustracja 20. kontrolka menu zawiera listę kategorii i produktów (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Jak wspomniano wcześniej w tym samouczku, można programowo uzyskać dostęp do struktury mapy witryny za pomocą klasy SiteMap. Poniższy kod zwraca SiteMapNode głównej domyślnego dostawcy:

SiteMapNode root = SiteMap.RootNode;

Ponieważ AspNetXmlSiteMapProvider jest domyślnym dostawcą aplikacji, powyższy kod zwróci węzeł główny zdefiniowany w Web.sitemap. Aby odwołać się do dostawcy mapy witryny innego niż domyślny, użyj właściwości SiteMap klasy s Providers , takiej jak:

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

Gdzie name to nazwa niestandardowego dostawcy mapy witryny (Northwind dla naszej aplikacji sieci Web).

Aby uzyskać dostęp do elementu członkowskiego określonego dla dostawcy mapy witryny, należy użyć SiteMap.Providers["name"] do pobrania wystąpienia dostawcy, a następnie rzutowania go na odpowiedni typ. Na przykład aby wyświetlić Właściwość NorthwindSiteMapProvider s CachedDate na stronie ASP.NET, użyj następującego kodu:

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

Note

Pamiętaj, aby przetestować funkcję zależności pamięci podręcznej SQL. Po odwiedzeniu stron Default.aspx, ProductsByCategory.aspxi ProductDetails.aspx przejdź do jednego z samouczków w sekcji Edytowanie, wstawianie i usuwanie, a następnie Edytuj nazwę kategorii lub produktu. Następnie wróć do jednej ze stron w folderze SiteMapProvider. Przy założeniu, że przed przekazaniem przez mechanizm sondowania zmian w źródłowej bazie danych zostanie zanotowany wystarczająco dużo czasu, należy zaktualizować mapę lokacji, aby pokazać nową nazwę produktu lub kategorii.

Podsumowanie

ASP.NET 2,0 s funkcje mapy witryny obejmują klasę SiteMap, wiele wbudowanych kontrolek sieci Web nawigacji i domyślnego dostawcę mapy witryny, który oczekuje informacji o mapie lokacji utrwalonych w pliku XML. Aby można było korzystać z informacji o mapie witryny z innego źródła, takiego jak baza danych, Architektura aplikacji lub zdalna usługa sieci Web, należy utworzyć niestandardowego dostawcę mapy witryny. Obejmuje to Tworzenie klasy, która dziedziczy bezpośrednio lub pośrednio z klasy SiteMapProvider.

W tym samouczku przedstawiono sposób tworzenia niestandardowego dostawcy mapy witryny, który bazuje na mapie witryny na informacje o produkcie i kategorii, które pochodzą z architektury aplikacji. Nasz dostawca rozszerzył klasę StaticSiteMapProvider i wymagała utworzenia metody BuildSiteMap, która pobrała dane, konstruuje hierarchię mapy lokacji i buforuje strukturę uzyskaną w zmiennej na poziomie klasy. Użyto zależności pamięci podręcznej SQL z funkcją wywołania zwrotnego, aby unieważniać buforowaną strukturę w przypadku zmodyfikowania bazowej Categories lub Products danych.

Szczęśliwe programowanie!

Dalsze informacje

Aby uzyskać więcej informacji na temat tematów omówionych w tym samouczku, zapoznaj się z następującymi zasobami:

Informacje o autorze

Scott Mitchell, autor siedmiu grup ASP/ASP. NET Books i założyciel of 4GuysFromRolla.com, pracował z technologiami sieci Web firmy Microsoft od czasu 1998. Scott działa jako niezależny konsultant, trainer i składnik zapisywania. Jego Najnowsza książka to Sams ASP.NET 2,0 w ciągu 24 godzin. Można go osiągnąć w mitchell@4GuysFromRolla.com. lub za pośrednictwem swojego blogu, który można znaleźć w http://ScottOnWriting.NET.

Specjalne podziękowania

Ta seria samouczków została sprawdzona przez wielu przydatnych recenzentów. Recenzenci liderzy dla tego samouczka to Dave Gardner, Zack Kowalski, Teresa Murphy i Bernadette Leigh. Chcesz przeglądać moje nadchodzące artykuły MSDN? Jeśli tak, upuść mi linię w mitchell@4GuysFromRolla.com.