Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
tarafından Scott Mitchell
ASP.NET 2.0'daki varsayılan site haritası sağlayıcısı, verilerini statik xml dosyasından alır. XML tabanlı sağlayıcı birçok küçük ve orta ölçekli Web sitesi için uygun olsa da, daha büyük Web uygulamaları daha dinamik bir site haritası gerektirir. Bu öğreticide, verilerini İş Mantığı Katmanı'ndan alan ve veritabanından veri alan özel bir site haritası sağlayıcısı oluşturacağız.
Giriş
ASP.NET 2.0 sn site haritası özelliği, sayfa geliştiricinin xml dosyası gibi kalıcı bir ortamda bir web uygulaması site haritası tanımlamasını sağlar. Tanımlandıktan sonra, site haritası verilerine ad alanı içindeki SiteMap
System.Web
sınıf aracılığıyla veya SiteMapPath, Menü ve TreeView denetimleri gibi çeşitli gezinti Web denetimleri aracılığıyla program aracılığıyla erişilebilir. Site haritası sistemi, farklı site haritası serileştirme uygulamalarının oluşturulabilmesi ve bir web uygulamasına takılabilmesi için sağlayıcı modelini kullanır. ASP.NET 2.0 ile birlikte gelen varsayılan site haritası sağlayıcısı, site eşleme yapısını bir XML dosyasında kalıcı hale döndürür. Ana Sayfalar ve Site Gezintisi öğreticisine geri dönüp bu yapıyı içeren ve XML'sini her yeni öğretici bölümüyle güncelleştiren adlı Web.sitemap
bir dosya oluşturduk.
Site haritasının yapısı bu öğreticiler gibi oldukça statikse, varsayılan XML tabanlı site haritası sağlayıcısı iyi çalışır. Ancak birçok senaryoda daha dinamik bir site haritası gereklidir. Şekil 1'de gösterilen ve her kategorinin ve ürünün web sitesinin yapısında bölümler olarak göründüğü site haritasını göz önünde bulundurun. Bu site haritasıyla, kök düğüme karşılık gelen web sayfasını ziyaret etmek tüm kategorileri listelese de, belirli bir kategorinin web sayfasını ziyaret etmek söz konusu kategorinin ürünlerini listeler ve belirli bir ürünün web sayfasını görüntülemek bu ürünün ayrıntılarını gösterir.
Şekil 1: Kategoriler ve Ürünler Site Haritası Yapısı Makyajı (Tam boyutlu görüntüyü görüntülemek için tıklayın)
Bu kategori ve ürün tabanlı yapı dosyaya Web.sitemap
sabit kodlanmış olsa da, bir kategori veya ürün her eklendiğinde, kaldırıldığında veya yeniden adlandırıldığında dosyanın güncelleştirilmesi gerekir. Sonuç olarak, yapısı veritabanından veya ideal olarak uygulama mimarisinin İş Mantığı Katmanı'ndan alınırsa site haritası bakımı büyük ölçüde basitleştirilir. Bu şekilde, ürünler ve kategoriler eklendikçe, yeniden adlandırıldıkça veya silindikçe, site haritası bu değişiklikleri yansıtacak şekilde otomatik olarak güncelleştirilir.
ASP.NET 2.0 s site haritası serileştirmesi sağlayıcı modelinin üzerinde oluşturulduğundan, verilerini veritabanı veya mimari gibi alternatif bir veri deposundan alan kendi özel site haritası sağlayıcımızı oluşturabiliriz. Bu öğreticide, verilerini BLL'den alan özel bir sağlayıcı oluşturacağız. Haydi başlayalım!
Not
Bu öğreticide oluşturulan özel site haritası sağlayıcısı, uygulamanın mimarisine ve veri modeline sıkı sıkıya bağlıdır. Jeff Prosise , Site Haritalarını SQL Server'da ve Beklediğiniz SQL Site Haritası Sağlayıcısı makalelerinde site haritası verilerini SQL Server'da depolamak için genelleştirilmiş bir yaklaşımı inceler.
1. Adım: Özel Site Haritası Sağlayıcısı Web Sayfaları Oluşturma
Özel site haritası sağlayıcısı oluşturmaya başlamadan önce bu öğretici için ihtiyacımız olan ASP.NET sayfalarını ekleyelim. adlı SiteMapProvider
yeni bir klasör ekleyerek başlayın. Ardından, aşağıdaki ASP.NET sayfaları bu klasöre ekleyerek her sayfayı ana sayfayla ilişkilendirdiğinizden Site.master
emin olun:
Default.aspx
ProductsByCategory.aspx
ProductDetails.aspx
Ayrıca klasöre bir CustomProviders
alt klasör App_Code
ekleyin.
Şekil 2: Site Haritası Sağlayıcısıyla İlgili Öğreticiler için ASP.NET Sayfaları Ekleme
Bu bölüm için yalnızca bir öğretici olduğundan, bölümün öğreticilerini listelememiz gerekmez Default.aspx
. Bunun yerine, Default.aspx
kategorileri GridView denetiminde görüntüler. 2. Adımda bunu ele alacağız.
Ardından, sayfaya bir başvuru içerecek şekilde güncelleştirin Web.sitemap
Default.aspx
. Özellikle, Önbelleğe Alma'nın <siteMapNode>
ardından aşağıdaki işaretlemeyi ekleyin:
<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." />
güncelleştirdikten Web.sitemap
sonra, öğreticiler web sitesini bir tarayıcı üzerinden görüntülemek için biraz bekleyin. Soldaki menü artık tek site haritası sağlayıcısı öğreticisi için bir öğe içeriyor.
Şekil 3: Site Haritası Artık Site Haritası Sağlayıcısı Öğreticisi için Bir Giriş Içeriyor
Bu öğreticinin ana odağı, özel bir site haritası sağlayıcısı oluşturma ve bir web uygulamasını bu sağlayıcıyı kullanacak şekilde yapılandırmayı göstermektir. Özellikle, Şekil 1'de gösterildiği gibi, her kategori ve ürün için bir düğümle birlikte kök düğüm içeren bir site haritası döndüren bir sağlayıcı oluşturacağız. Genel olarak, site haritasındaki her düğüm bir URL belirtebilir. Site haritamız için kök düğümün URL'si olur ~/SiteMapProvider/Default.aspx
ve bu url veritabanındaki tüm kategorileri listeler. Site haritasındaki her kategori düğümü, belirtilen ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
tüm ürünleri listeleyen öğesine işaret eden bir URL'ye sahip olur. Son olarak, her ürün sitesi haritası düğümü, belirli ürün ayrıntılarını görüntüleyen öğesini işaret ~/SiteMapProvider/ProductDetails.aspx?ProductID=productID
eder.
Başlamak için , Default.aspx
ve ProductsByCategory.aspx
sayfalarını oluşturmamız ProductDetails.aspx
gerekir. Bu sayfalar sırasıyla Adım 2, 3 ve 4'te tamamlanır. Bu öğreticinin itici gücü site haritası sağlayıcılarına ait olduğundan ve geçmiş öğreticilerde bu tür çok sayfalı ana/ayrıntı raporları oluşturmayı ele aldığımızdan, 2 ile 4 arası adımlarda acele edeceğiz. Birden çok sayfaya yayılan ana/ayrıntı raporları oluşturma konusunda yenileyiciye ihtiyacınız varsa, İki Sayfada Ana/Ayrıntı Filtreleme öğreticisine geri dönün.
2. Adım: Kategori Listesi Görüntüleme
Klasöründeki sayfayı Default.aspx
açın ve Toolbox'tan SiteMapProvider
Bir GridView'ı Tasarımcı'ya sürükleyip olarak ayarlayınID
Categories
. GridView'un akıllı etiketinden bunu adlı CategoriesDataSource
yeni bir ObjectDataSource'a bağlayın ve sınıf CategoriesBLL
yöntemini kullanarak GetCategories
verilerini alacak şekilde yapılandırın. Bu GridView yalnızca kategorileri görüntülediğinden ve veri değiştirme özellikleri sağlamadığından, UPDATE, INSERT ve DELETE sekmelerindeki açılan listeleri (Yok) olarak ayarlayın.
Şekil 4: Yöntemi Kullanarak ObjectDataSource'un Kategorileri Döndürecek Şekilde Yapılandırılması GetCategories
(Tam boyutlu görüntüyü görüntülemek için tıklayın)
Şekil 5: UPDATE, INSERT ve DELETE Sekmelerindeki Açılan Listeleri (Yok) olarak ayarlayın (Tam boyutlu görüntüyü görüntülemek için tıklayın)
Veri Kaynağını Yapılandırma sihirbazını tamamladıktan sonra Visual Studio , , CategoryID
, CategoryName
Description
ve NumberOfProducts
için BrochurePath
bir BoundField ekler. GridView'ı yalnızca ve BoundField'lerini barındıracak CategoryName
şekilde düzenleyin ve BoundField özelliğini Description
Category olarak güncelleştirinCategoryName
.HeaderText
Ardından, bir HyperLinkField ekleyin ve en soldaki alan olacak şekilde konumlandırın.
DataNavigateUrlFields
özelliğini olarak CategoryID
ve DataNavigateUrlFormatString
özelliğini olarak ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}
ayarlayın.
Text
özelliğini Ürünleri Görüntüle olarak ayarlayın.
Şekil 6: GridView'a Categories
HyperLinkField ekleme
ObjectDataSource oluşturulduktan ve GridView alanlarını özelleştirdikten sonra, bildirim temelli işaretleme iki denetim aşağıdaki gibi görünür:
<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>
Şekil 7'de tarayıcı üzerinden görüntülendiğinde gösterilmektedir Default.aspx
. Bir kategorinin Ürünleri Görüntüle bağlantısına ProductsByCategory.aspx?CategoryID=categoryID
tıkladığınızda, adım 3'te oluşturacağımız adresine gelirsiniz.
Şekil 7: Her Kategori, Ürünleri Görüntüle Bağlantısıyla Birlikte Listelenir (Tam boyutlu görüntüyü görüntülemek için tıklayın)
3. Adım: Seçili Kategorinin Ürünlerini Listeleme
Sayfayı ProductsByCategory.aspx
açın ve adını vererek ProductsByCategory
bir GridView ekleyin. Akıllı etiketinden GridView'ı adlı ProductsByCategoryDataSource
yeni bir ObjectDataSource'a bağlayın. ObjectDataSource'ı sınıf ProductsBLL
yöntemini kullanacak GetProductsByCategoryID(categoryID)
şekilde yapılandırın ve UPDATE, INSERT ve DELETE sekmelerinde açılan listeleri (Yok) olarak ayarlayın.
Şekil 8: Sınıf ProductsBLL
Yöntemini kullanma GetProductsByCategoryID(categoryID)
(Tam boyutlu görüntüyü görüntülemek için tıklayın)
Veri Kaynağını Yapılandırma sihirbazının son adımında categoryID için parametre kaynağı istenir. Bu bilgiler querystring alanından CategoryID
geçirildiğinden, açılan listeden QueryString'i seçin ve Şekil 9'da gösterildiği gibi QueryStringField metin kutusuna CategoryID girin. Sihirbazı tamamlamak için Son’a tıklayın.
Şekil 9: categoryIDCategoryID
Sorgu Dizesi Alanını Kullanma (Tam boyutlu görüntüyü görüntülemek için tıklayın)
Sihirbazı tamamladıktan sonra Visual Studio, ürün veri alanları için GridView'a karşılık gelen BoundField'leri ve CheckBoxField'ı ekler. , ve ProductName
UnitPrice
BoundFields dışında SupplierName
tümünü kaldırın. Bu üç BoundFields HeaderText
özelliğini sırasıyla Product, Price ve Supplier değerlerini okuyacak şekilde özelleştirin. BoundField'i UnitPrice
para birimi olarak biçimlendirin.
Ardından bir HyperLinkField ekleyin ve en soldaki konuma taşıyın. Özelliğini Text
Ayrıntıları Görüntüle, DataNavigateUrlFields
özelliğini olarak ProductID
ve DataNavigateUrlFormatString
özelliğini olarak ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}
ayarlayın.
Şekil 10: İşaret Eden Bir Görünüm Ayrıntıları HyperLinkField Ekleme ProductDetails.aspx
Bu özelleştirmeleri yaptıktan sonra GridView ve ObjectDataSource bildirim temelli işaretlemesi aşağıdakine benzer olmalıdır:
<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>
Tarayıcı üzerinden görüntülemeye Default.aspx
dönün ve İçecekler için Ürünleri Görüntüle bağlantısına tıklayın. Bu sizi ProductsByCategory.aspx?CategoryID=1
, Northwind veritabanındaki İçecekler kategorisine ait ürünlerin adlarını, fiyatlarını ve tedarikçilerini görüntüleyen bölümüne götürür (bkz. Şekil 11). Kullanıcıları kategori listeleme sayfasına (Default.aspx
) döndürmek için bir bağlantı ve seçili kategorinin adını ve açıklamasını görüntüleyen detailsview veya FormView denetimini içerecek şekilde bu sayfayı daha da geliştirebilirsiniz.
Şekil 11: İçecek Adları, Fiyatlar ve Tedarikçiler Görüntülenir (Tam boyutlu görüntüyü görüntülemek için tıklayın)
4. Adım: Ürün Ayrıntılarını Gösterme
Son sayfa olan ProductDetails.aspx
, seçili ürün ayrıntılarını görüntüler. Araç Kutusundan DetailsView'ı açın ProductDetails.aspx
ve Tasarımcı'ya sürükleyin. DetailsView s özelliğini olarak ID
ayarlayın ve ve ProductInfo
özellik değerlerini temizleyinHeight
.Width
Akıllı etiketinden DetailsView'ı adlı ProductDataSource
yeni bir ObjectDataSource'a bağlayın ve ObjectDataSource'un verilerini sınıfın ProductsBLL
GetProductByProductID(productID)
yönteminden çekecek şekilde yapılandırın. 2. ve 3. Adımlarda oluşturulan önceki web sayfalarında olduğu gibi, UPDATE, INSERT ve DELETE sekmelerindeki açılan listeleri (Yok) olarak ayarlayın.
Şekil 12: ObjectDataSource'un Yöntemini Kullanacak Şekilde Yapılandırılması GetProductByProductID(productID)
(Tam boyutlu görüntüyü görüntülemek için tıklayın)
Veri Kaynağını Yapılandırma sihirbazının son adımında productID parametresinin kaynağı istenir. Bu veriler querystring alanı ProductID
üzerinden geldiğinden, açılan listeyi QueryString ve QueryStringField metin kutusunu ProductID olarak ayarlayın. Son olarak, sihirbazı tamamlamak için Son düğmesine tıklayın.
Şekil 13: ProductID(ProductID
görüntülemek için tıklayın)
Veri Kaynağını Yapılandırma sihirbazını tamamladıktan sonra Visual Studio, ürün veri alanları için DetailsView'da karşılık gelen BoundField'leri ve bir CheckBoxField'ı oluşturur.
ProductID
, SupplierID
ve BoundField değerlerini kaldırın ve CategoryID
kalan alanları uygun gördüğünüz şekilde yapılandırın. Birkaç estetik yapılandırmadan sonra DetailsView ve ObjectDataSource bildirim temelli işaretlemem aşağıdaki gibi görünüyordu:
<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>
Bu sayfayı test etmek için İçecekler kategorisi için Ürünleri Görüntüle'ye Default.aspx
geri dönün ve tıklayın. İçecek ürünleri listesinden Chai Tea için Ayrıntıları Görüntüle bağlantısına tıklayın. Bu sizi ProductDetails.aspx?ProductID=1
Chai Tea s ayrıntılarını gösteren adresine götürür (bkz. Şekil 14).
Şekil 14: Chai Tea s Supplier, Category, Price ve Diğer Bilgiler Görüntüleniyor (Tam boyutlu görüntüyü görüntülemek için tıklayın)
5. Adım: Site Haritası Sağlayıcısının İç Çalışmalarını Anlama
Site haritası, web sunucusunun belleğinde hiyerarşi oluşturan örneklerin SiteMapNode
koleksiyonu olarak temsil edilir. Tam olarak bir kök olmalıdır, kök olmayan tüm düğümler tam olarak bir üst düğüme sahip olmalıdır ve tüm düğümler rastgele sayıda alt düğüme sahip olabilir. Her SiteMapNode
nesne web sitesi yapısındaki bir bölümü temsil eder; bu bölümler genellikle buna karşılık gelen bir web sayfasına sahiptir. Sonuç olarak, sınıfın SiteMapNode
temsil ettiği bölüm Title
için bilgi sağlayan , Url
ve Description
gibi SiteMapNode
özellikleri vardır. Ayrıca hiyerarşideki her Key
bir özelliği ve bu hiyerarşiyi SiteMapNode
, , ChildNodes
, ParentNode
, NextSibling
vb. oluşturmak için kullanılan özellikleri benzersiz olarak tanımlayan bir PreviousSibling
özellik vardır.
Şekil 15'te Şekil 1'deki genel site haritası yapısı gösterilir, ancak uygulama ayrıntıları daha ayrıntılı olarak çizilir.
Şekil 15: Her SiteMapNode
birinin , Title
, Url
ve Benzeri Özellikler Key
Vardır (Tam boyutlu görüntüyü görüntülemek için tıklayın)
Site haritasına ad alanında SiteMap
sınıfıSystem.Web
erişilebilir. Bu sınıf s RootNode
özelliği, site eşlemesinin kök SiteMapNode
örneğini döndürür; CurrentNode
özelliği şu anda istenen sayfanın URL'si ile eşleşen öğesini döndürür SiteMapNode
Url
. Bu sınıf, ASP.NET 2.0 s gezinti Web denetimleri tarafından dahili olarak kullanılır.
Sınıf özelliklerine SiteMap
erişildiğinde, site haritası yapısını kalıcı bir ortamdan belleğe seri hale getirmesi gerekir. Ancak, site haritası serileştirme mantığı sınıfına SiteMap
sabit kodlanmaz. Bunun yerine, çalışma zamanında SiteMap
sınıf, serileştirme için hangi site haritası sağlayıcısının kullanılacağını belirler. Varsayılan olarak, XmlSiteMapProvider
düzgün biçimlendirilmiş bir XML dosyasından site haritasının yapısını okuyan sınıfı kullanılır. Ancak, biraz çalışmayla kendi özel site haritası sağlayıcımızı oluşturabiliriz.
Tüm site haritası sağlayıcıları, site haritası sağlayıcıları için gerekli temel yöntemleri ve özellikleri içeren, ancak uygulama ayrıntılarının çoğunu atlayan sınıfındanSiteMapProvider
İkinci sınıf olan StaticSiteMapProvider
, sınıfını SiteMapProvider
genişletir ve gerekli işlevselliğin daha sağlam bir uygulamasını içerir. dahili olarak, StaticSiteMapProvider
site haritasının örneklerini içinde SiteMapNode
depolar Hashtable
ve gibi AddNode(child, parent)
RemoveNode(siteMapNode),
yöntemler sağlar ve Clear()
iç öğesine s SiteMapNode
ekleyip kaldırırHashtable
.
XmlSiteMapProvider
, 'den StaticSiteMapProvider
türetilir.
genişleten StaticSiteMapProvider
bir özel site haritası sağlayıcısı oluştururken, geçersiz kılınması gereken iki soyut yöntem vardır: BuildSiteMap
ve GetRootNodeCore
.
BuildSiteMap
adından da anlaşılacağı gibi, site haritası yapısını kalıcı depolamadan yüklemek ve bellekte oluşturmaktan sorumludur.
GetRootNodeCore
, site haritasında kök düğümü döndürür.
Bir web uygulamasının site haritası sağlayıcısını kullanabilmesi için önce uygulamanın yapılandırmasına kaydedilmesi gerekir. Varsayılan olarak, XmlSiteMapProvider
sınıfı adı AspNetXmlSiteMapProvider
kullanılarak kaydedilir. Ek site haritası sağlayıcılarını kaydetmek için aşağıdaki işaretlemeyi öğesine Web.config
ekleyin:
<configuration>
<system.web>
...
<siteMap defaultProvider="defaultProviderName">
<providers>
<add name="name" type="type" />
</providers>
</siteMap>
</system.web>
</configuration>
Ad değeri sağlayıcıya insan tarafından okunabilir bir ad atarken tür, site haritası sağlayıcısının tam tür adını belirtir. Özel site haritası sağlayıcımızı oluşturduktan sonra 7. Adımda ad ve tür değerleri için somut değerleri keşfedeceğiz.
Site haritası sağlayıcı sınıfı, sınıfından ilk kez erişildiğinde SiteMap
örneği oluşturulur ve web uygulamasının ömrü boyunca bellekte kalır. Site haritası sağlayıcısının birden çok eşzamanlı web sitesi ziyaretçisinden çağrılabilecek tek bir örneği olduğundan, sağlayıcı yöntemlerinin iş parçacığı açısından güvenli olması zorunludur.
Performans ve ölçeklenebilirlik nedenleriyle, bellek içi site eşleme yapısını önbelleğe alıp yöntemi her çağrıldığında yeniden oluşturmadan bu önbelleğe alınmış yapıyı BuildSiteMap
döndürmemiz önemlidir.
BuildSiteMap
sayfada kullanımda olan gezinti denetimlerine ve site haritası yapısının derinliğine bağlı olarak, kullanıcı başına sayfa isteği başına birkaç kez çağrılabilir. Her durumda, site haritası yapısını BuildSiteMap
her çağrıldığında önbelleğe almazsak, mimariden ürün ve kategori bilgilerini yeniden almamız gerekir (bu da veritabanına sorgu yapılmasına neden olur). Önceki önbelleğe alma öğreticilerinde ele aldığımız gibi önbelleğe alınan veriler eskitilebilir. Bununla mücadele etmek için zaman veya SQL önbelleği bağımlılığı tabanlı süre sonu kullanabiliriz.
Not
Site haritası sağlayıcısı isteğe bağlı olarak yönteminiInitialize
Initialize
, site haritası sağlayıcısı ilk kez başlatıldığında çağrılır ve öğesinde Web.config
<add>
sağlayıcıya atanmış olan özel öznitelikler geçirilir: <add name="name" type="type" customAttribute="value" />
. Bir sayfa geliştiricinin sağlayıcı kodunu değiştirmek zorunda kalmadan site haritası sağlayıcısıyla ilgili çeşitli ayarları belirtmesine izin vermek istiyorsanız kullanışlıdır. Örneğin, kategori ve ürün verilerini mimarinin aksine doğrudan veritabanından okuyor olsaydık, sağlayıcı kodunda sabit kodlanmış bir değer kullanmak yerine sayfa geliştiricisinin veritabanı bağlantı dizesi Web.config
belirtmesine izin vermek isteyebiliriz. 6. Adımda oluşturacağımız özel site haritası sağlayıcısı bu Initialize
yöntemi geçersiz kılmaz. yöntemini kullanma Initialize
örneği için Jeff Prosise'in Site Haritalarını SQL Server'da depolama makalesine bakın.
6. Adım: Özel Site Haritası Sağlayıcısı Oluşturma
Northwind veritabanındaki kategoriler ve ürünlerden site haritasını oluşturan özel bir site haritası sağlayıcısı oluşturmak için, öğesini genişleten StaticSiteMapProvider
bir sınıf oluşturmamız gerekir. 1. Adımda, klasörüne bir CustomProviders
klasör App_Code
eklemenizi istedim- bu klasöre adlı NorthwindSiteMapProvider
yeni bir sınıf ekleyin. Sınıfına NorthwindSiteMapProvider
aşağıdaki kodu ekleyin:
Imports System.Web
Imports System.Web.Caching
Public Class NorthwindSiteMapProvider
Inherits StaticSiteMapProvider
Private ReadOnly siteMapLock As New Object()
Private root As SiteMapNode = Nothing
Public Const CacheDependencyKey As String = "NorthwindSiteMapProviderCacheDependency"
Public Overrides Function BuildSiteMap() As System.Web.SiteMapNode
' Use a lock to make this method thread-safe
SyncLock siteMapLock
' First, see if we already have constructed the
' rootNode. If so, return it...
If root IsNot Nothing Then
Return root
End If
' We need to build the site map!
' Clear out the current site map structure
MyBase.Clear()
' Get the categories and products information from the database
Dim productsAPI As New ProductsBLL()
Dim products As Northwind.ProductsDataTable = productsAPI.GetProducts()
' Create the root SiteMapNode
root = New SiteMapNode( _
Me, "root", "~/SiteMapProvider/Default.aspx", "All Categories")
AddNode(root)
' Create SiteMapNodes for the categories and products
For Each product As Northwind.ProductsRow In products
' Add a new category SiteMapNode, if needed
Dim categoryKey, categoryName As String
Dim createUrlForCategoryNode As Boolean = True
If product.IsCategoryIDNull() Then
categoryKey = "Category:None"
categoryName = "None"
createUrlForCategoryNode = False
Else
categoryKey = String.Concat("Category:", product.CategoryID)
categoryName = product.CategoryName
End If
Dim categoryNode As SiteMapNode = FindSiteMapNodeFromKey(categoryKey)
' Add the category SiteMapNode if it does not exist
If categoryNode Is Nothing Then
Dim productsByCategoryUrl As String = String.Empty
If createUrlForCategoryNode Then
productsByCategoryUrl = _
"~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=" & _
product.CategoryID
End If
categoryNode = New SiteMapNode _
(Me, categoryKey, productsByCategoryUrl, categoryName)
AddNode(categoryNode, root)
End If
' Add the product SiteMapNode
Dim productUrl As String = _
"~/SiteMapProvider/ProductDetails.aspx?ProductID=" & _
product.ProductID
Dim productNode As New SiteMapNode _
(Me, String.Concat("Product:", product.ProductID), _
productUrl, product.ProductName)
AddNode(productNode, categoryNode)
Next
' Add a "dummy" item to the cache using a SqlCacheDependency
' on the Products and Categories tables
Dim productsTableDependency As New _
System.Web.Caching.SqlCacheDependency("NorthwindDB", "Products")
Dim categoriesTableDependency As New _
System.Web.Caching.SqlCacheDependency("NorthwindDB", "Categories")
' Create an AggregateCacheDependency
Dim aggregateDependencies As New System.Web.Caching.AggregateCacheDependency()
aggregateDependencies.Add(productsTableDependency, categoriesTableDependency)
' Add the item to the cache specifying a callback function
HttpRuntime.Cache.Insert( _
CacheDependencyKey, DateTime.Now, aggregateDependencies, _
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, _
CacheItemPriority.Normal, AddressOf OnSiteMapChanged)
' Finally, return the root node
Return root
End SyncLock
End Function
Protected Overrides Function GetRootNodeCore() As System.Web.SiteMapNode
Return BuildSiteMap()
End Function
Protected Sub OnSiteMapChanged _
(key As String, value As Object, reason As CacheItemRemovedReason)
SyncLock siteMapLock
If String.Compare(key, CacheDependencyKey) = 0 Then
' Refresh the site map
root = Nothing
End If
End SyncLock
End Sub
Public ReadOnly Property CachedDate() As Nullable(Of DateTime)
Get
Dim value As Object = HttpRuntime.Cache(CacheDependencyKey)
If value Is Nothing OrElse Not TypeOf value Is Nullable(Of DateTime) Then
Return Nothing
Else
Return CType(value, Nullable(Of DateTime))
End If
End Get
End Property
End Class
Bir deyimiyle başlayan bu sınıf yöntemini BuildSiteMap
keşfetmekle başlayalımlock
.
lock
deyimi bir kerede yalnızca bir iş parçacığının girmesine izin verir, böylece koduna erişimi seri hale getirerek iki eş zamanlı iş parçacığının birbirine adım atmasını engeller.
Sınıf düzeyi SiteMapNode
değişkeni root
, site eşleme yapısını önbelleğe almak için kullanılır. Site haritası ilk kez oluşturulduğunda veya temel alınan veriler değiştirildikten root
Nothing
sonra ilk kez oluşturulur ve site haritası yapısı oluşturulur. Site haritasının kök düğümü, bu yöntemin bir sonraki çağrılışında olarak root
adlandırılmaması içinroot
, Nothing
oluşturma işlemi sırasında atanır. Sonuç olarak, site haritası yapısı olmadığı root
sürece Nothing
yeniden oluşturmak zorunda kalmadan çağırana döndürülür.
Kök ise Nothing
, site haritası yapısı ürün ve kategori bilgilerinden oluşturulur. Site haritası, örnekleri oluşturup SiteMapNode
ardından sınıf StaticSiteMapProvider
yöntemine yapılan çağrılar aracılığıyla hiyerarşiyi AddNode
oluşturarak oluşturulur.
AddNode
, çeşitli örnekleri bir SiteMapNode
içinde depolayarak Hashtable
iç muhasebeyi gerçekleştirir. Hiyerarşiyi oluşturmadan önce, öğesini iç Clear
öğesinden temizleyen yöntemini çağırarak Hashtable
başlayacağız. Ardından, ProductsBLL
s sınıfı GetProducts
yöntemi ve sonuçta elde ProductsDataTable
edilenler yerel değişkenlerde depolanır.
Site haritasının yapısı, kök düğümü oluşturup öğesine root
atayarak başlar. Burada ve bu SiteMapNode
işlem boyunca kullanılan s oluşturucununBuildSiteMap
aşırı yüklemesi aşağıdaki bilgiler geçirilir:
- Site haritası sağlayıcısına (
Me
) bir başvuru. - s
SiteMapNode
Key
. Bu gerekli değer herSiteMapNode
için benzersiz olmalıdır. - s
SiteMapNode
Url
.Url
isteğe bağlıdır, ancak sağlanırsa herSiteMapNode
değerinUrl
benzersiz olması gerekir. -
SiteMapNode
Gerekli olan sTitle
.
yöntem AddNode(root)
çağrısı, SiteMapNode
root
öğesini kök olarak site haritasına ekler. Ardından içindeki her ProductRow
ProductsDataTable
biri numaralandırılır. Geçerli ürün kategorisi için zaten bir SiteMapNode
varsa, buna başvurulur. Aksi takdirde, yöntem çağrısı aracılığıyla kategorisi için yeni SiteMapNode
bir oluşturulur ve öğesinin SiteMapNode``root
AddNode(categoryNode, root)
alt öğesi olarak eklenir. Uygun kategori SiteMapNode
düğümü bulunduktan veya oluşturulduktan sonra, geçerli ürün için bir SiteMapNode
oluşturulur ve aracılığıyla SiteMapNode
kategorinin AddNode(productNode, categoryNode)
alt öğesi olarak eklenir. Kategorinin SiteMapNode
Url
özellik değerinin, ürün ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
SiteMapNode
özelliği atanırken Url
olduğuna ~/SiteMapNode/ProductDetails.aspx?ProductID=productID
dikkat edin.
Not
Bunlar NULL
için veritabanı CategoryID
değerine sahip olan ürünler, özelliği Yok olarak ayarlanmış ve SiteMapNode
özelliği boş bir dize olarak ayarlanmış bir kategori Title
Url
altında gruplandırılır. Sınıfın Url
ProductBLL
yöntemi şu anda yalnızca bir değere sahip olan ürünleri döndürme özelliğinden yoksun olduğundan boş bir GetProductsByCategory(categoryID)
NULL
dizeye ayarlamaya CategoryID
karar verdim. Ayrıca, gezinti denetimlerinin özelliği için SiteMapNode
bir değeri olmayan bir Url
öğesini nasıl işlendiğini göstermek istedim. None SiteMapNode
s Url
özelliğinin yalnızca değerleri olan ProductsByCategory.aspx
NULL
ürünleri görüntülemesi için CategoryID
bu öğreticiyi genişletmenizi tavsiye ederim.
Site haritası oluşturulduktan sonra, bir nesne aracılığıyla ve Categories
tablolarında SQL önbellek bağımlılığı kullanılarak veri önbelleğine Products
rastgele bir AggregateCacheDependency
nesne eklenir. Önceki öğreticide SQL Önbellek Bağımlılıklarını Kullanma öğreticisinde SQL önbellek bağımlılıklarını kullanmayı keşfettik. Ancak özel site haritası sağlayıcısı henüz araştırmadığımız veri önbelleği Insert
yönteminin aşırı yüklemesini kullanır. Bu aşırı yükleme, nesne önbellekten kaldırıldığında çağrılan bir temsilcinin son giriş parametresi olarak kabul eder. Özellikle, sınıfında daha aşağıda tanımlanan yönteme CacheItemRemovedCallback
işaret eden yeni geçiririzOnSiteMapChanged
.
Not
Site eşlemesinin bellek içi gösterimi sınıf düzeyi değişkeni root
aracılığıyla önbelleğe alınır. Özel site haritası sağlayıcı sınıfının yalnızca bir örneği olduğundan ve bu örnek web uygulamasındaki tüm iş parçacıkları arasında paylaşıldığından, bu sınıf değişkeni önbellek görevi görür.
BuildSiteMap
yöntemi veri önbelleğini de kullanır, ancak yalnızca veya Categories
tablolarındaki temel veritabanı verileri değiştiğinde Products
bildirim almak için bir araç olarak kullanılır. Veri önbelleğine koyulan değerin yalnızca geçerli tarih ve saat olduğunu unutmayın. Gerçek site haritası verileri veri önbelleğine yerleştirilmemiştir .
yöntemi, BuildSiteMap
site haritasının kök düğümünü döndürerek tamamlar.
Kalan yöntemler oldukça basittir.
GetRootNodeCore
kök düğümü döndürmekten sorumludur. Kök BuildSiteMap
döndürdüğünden, GetRootNodeCore
yalnızca s dönüş değerini döndürür BuildSiteMap
.
OnSiteMapChanged
yöntemi, önbellek öğesinin ne zaman kaldırılacağına root
geri dönerNothing
. Kök yeniden olarak Nothing
ayarlandığında, bir sonraki çağrıda BuildSiteMap
site haritası yapısı yeniden oluşturulur. Son olarak özelliği, CachedDate
böyle bir değer varsa veri önbelleğinde depolanan tarih ve saat değerini döndürür. Bu özellik, site haritası verilerinin en son ne zaman önbelleğe alındığını belirlemek için bir sayfa geliştiricisi tarafından kullanılabilir.
7. Adım:NorthwindSiteMapProvider
Web uygulamamızın 6. Adımda oluşturulan site haritası sağlayıcısını kullanabilmesi NorthwindSiteMapProvider
için bu siteyi <siteMap>
bölümüne Web.config
kaydetmemiz gerekir. Özellikle, öğesinde aşağıdaki işaretlemeyi <system.web>
Web.config
ekleyin:
<siteMap defaultProvider="AspNetXmlSiteMapProvider">
<providers>
<add name="Northwind" type="NorthwindSiteMapProvider" />
</providers>
</siteMap>
Bu işaretleme iki şey yapar: birincisi, yerleşikin AspNetXmlSiteMapProvider
varsayılan site haritası sağlayıcısı olduğunu gösterir; ikincisi, 6. Adımda oluşturulan özel site haritası sağlayıcısını insan dostu Northwind adıyla kaydeder.
Not
Uygulamanın App_Code
klasöründe bulunan site haritası sağlayıcıları için özniteliğin type
değeri yalnızca sınıf adıdır. Alternatif olarak, özel site haritası sağlayıcısı, derlenmiş derleme web uygulamasının /Bin
dizinine yerleştirilmiş ayrı bir Sınıf Kitaplığı projesinde oluşturulmuş olabilir. Bu durumda öznitelik type
değeri Ad Alanı olacaktır.ClassName, AssemblyName .
güncelleştirdikten Web.config
sonra, öğreticilerdeki herhangi bir sayfayı bir tarayıcıda görüntülemek için biraz bekleyin. Sol taraftaki gezinti arabiriminin içinde tanımlanan Web.sitemap
bölümleri ve öğreticileri yine de gösterdiğini unutmayın. Bunun nedeni varsayılan sağlayıcı olarak ayrılmamızdır AspNetXmlSiteMapProvider
. kullanan NorthwindSiteMapProvider
bir gezinti kullanıcı arabirimi öğesi oluşturmak için Northwind site haritası sağlayıcısının kullanılması gerektiğini açıkça belirtmemiz gerekir. 8. Adımda bunu nasıl başaracağımızı göreceğiz.
8. Adım: Özel Site Haritası Sağlayıcısını Kullanarak Site Haritası Bilgilerini Görüntüleme
özel site haritası sağlayıcısı oluşturulduktan ve içinde Web.config
kaydedildiyse, klasördeki , Default.aspx
ve ProductsByCategory.aspx
sayfalarına ProductDetails.aspx
gezinti denetimleri eklemeye SiteMapProvider
hazırız. Başlangıç olarak sayfayı Default.aspx
açın ve Araç Kutusu'ndan Tasarımcı'ya sürükleyin SiteMapPath
. SiteMapPath denetimi, Araç Kutusu'nun Gezinti bölümünde bulunur.
Şekil 16: 'a Default.aspx
SiteMapPath ekleme (Tam boyutlu görüntüyü görüntülemek için tıklayın)
Site HaritasıPath denetimi, site haritasındaki geçerli sayfanın konumunu gösteren bir içerik haritası görüntüler. Ana Sayfalar ve Site Gezintisi öğreticisinde ana sayfanın en üstüne bir SiteMapPath ekledik .
Bu sayfayı bir tarayıcı üzerinden görüntülemek için biraz zaman ayırın. Şekil 16'da eklenen Site Haritası Yolu, varsayılan site haritası sağlayıcısını kullanır ve verilerini konumundan Web.sitemap
çeker. Bu nedenle içerik haritası, tıpkı sağ üst köşedeki içerik haritasında olduğu gibi Site Haritasını Özelleştiren Giriş'i > gösterir.
Şekil 17: İçerik Haritası Varsayılan Site Haritası Sağlayıcısını Kullanır (Tam boyutlu görüntüyü görüntülemek için tıklayın)
Şekil 16'da SiteMapPath'in eklenmesi için, Adım 6'da oluşturduğumuz özel site haritası sağlayıcısını SiteMapProvider
kullanın, özelliğini içinde NorthwindSiteMapProvider
kendisine atadığımız Web.config
ad olan Northwind olarak ayarlayın. Ne yazık ki Tasarımcı varsayılan site haritası sağlayıcısını kullanmaya devam eder, ancak bu özellik değişikliğini yaptıktan sonra sayfayı bir tarayıcı üzerinden ziyaret ederseniz içerik haritasının artık özel site haritası sağlayıcısını kullandığını görürsünüz.
Şekil 18: İçerik Haritası Artık Özel Site Haritası Sağlayıcısını NorthwindSiteMapProvider
Kullanıyor (Tam boyutlu görüntüyü görüntülemek için tıklayın)
SiteMapPath denetimi ve ProductsByCategory.aspx
sayfalarında daha işlevsel bir kullanıcı arabirimi ProductDetails.aspx
görüntüler. Her ikisinde de özelliğini Northwind olarak ayarlayarak SiteMapProvider
bu sayfalara bir SiteMapPath ekleyin. İçecekler Default.aspx
için Ürünleri Görüntüle bağlantısına ve ardından Chai Tea için Ayrıntıları Görüntüle bağlantısına tıklayın. Şekil 19'da gösterildiği gibi içerik haritası geçerli site haritası bölümünü ( Chai Tea ) ve atalarını içerir: İçecekler ve Tüm Kategoriler .
Şekil 19: İçerik Haritası Artık Özel Site Haritası Sağlayıcısını NorthwindSiteMapProvider
Kullanıyor (Tam boyutlu görüntüyü görüntülemek için tıklayın)
Menu ve TreeView denetimleri gibi SiteMapPath'e ek olarak diğer gezinti kullanıcı arabirimi öğeleri kullanılabilir.
Default.aspx
Örneğin, bu öğreticinin indirmesindeki , ProductsByCategory.aspx
ve ProductDetails.aspx
sayfaları menü denetimlerini içerir (bkz. Şekil 20). ASP.NET 2.0'daki gezinti denetimlerine ve site haritası sistemine daha ayrıntılı bir bakış için ASP.NET 2.0 Hızlı Başlangıçları'nın ASP.NET 2.0'ın Gelişmiş Site Gezinti Özellikleri ve Site Gezinti Denetimlerini Kullanma bölümüne bakın.
Şekil 20: Menü Denetimi Kategorilerin ve Ürünlerin Her Birini Listeler (Tam boyutlu görüntüyü görüntülemek için tıklayın)
Bu öğreticide daha önce belirtildiği gibi, site haritası yapısına sınıf üzerinden SiteMap
program aracılığıyla erişilebilir. Aşağıdaki kod varsayılan sağlayıcının kökünü SiteMapNode
döndürür:
Dim root As SiteMapNode = SiteMap.RootNode
AspNetXmlSiteMapProvider
uygulamamız için varsayılan sağlayıcı olduğundan yukarıdaki kod içinde Web.sitemap
tanımlanan kök düğümü döndürür. Varsayılan dışında bir site haritası sağlayıcısına başvurmak için aşağıdaki gibi sınıf s SiteMap
kullanınProviders
:
Dim root As SiteMapNode = SiteMap.Providers("name").RootNode
Burada ad , özel site haritası sağlayıcısının adıdır (Web uygulamamız için Northwind).
Site haritası sağlayıcısına özgü bir üyeye erişmek için öğesini kullanarak SiteMap.Providers["name"]
sağlayıcı örneğini alın ve uygun türe yayınlayın. Örneğin, s NorthwindSiteMapProvider
özelliğini bir ASP.NET sayfasında görüntülemek CachedDate
için aşağıdaki kodu kullanın:
Dim customProvider As NorthwindSiteMapProvider = _
TryCast(SiteMap.Providers("Northwind"), NorthwindSiteMapProvider)
If customProvider IsNot Nothing Then
Dim lastCachedDate As Nullable(Of DateTime) = customProvider.CachedDate
If lastCachedDate.HasValue Then
SiteMapLastCachedDate.Text = _
"Site map cached on: " & lastCachedDate.Value.ToString()
Else
SiteMapLastCachedDate.Text = "The site map is being reconstructed!"
End If
End If
Not
SQL önbellek bağımlılığı özelliğini test etmeye dikkat edin. , Default.aspx
ve sayfalarını ziyaret ettikten ProductsByCategory.aspx
sonra Düzenleme, Ekleme ve ProductDetails.aspx
Silme bölümündeki öğreticilerden birine gidin ve bir kategori veya ürünün adını düzenleyin. Ardından klasördeki sayfalardan SiteMapProvider
birine dönün. Temel alınan veritabanındaki değişikliği not etmek için yoklama mekanizması için yeterli süre geçtiğini varsayarsak, site haritası yeni ürün veya kategori adını gösterecek şekilde güncelleştirilmelidir.
Özet
ASP.NET 2.0 sn site haritası özellikleri arasında bir SiteMap
sınıf, bir dizi yerleşik gezinti Web denetimi ve site haritası bilgilerinin xml dosyasında kalıcı olmasını bekleyen varsayılan bir site haritası sağlayıcısı bulunur. Veritabanı, uygulama mimarisi veya uzak Web hizmeti gibi başka bir kaynaktan site haritası bilgilerini kullanmak için özel bir site haritası sağlayıcısı oluşturmamız gerekir. Bu, doğrudan veya dolaylı olarak sınıfından SiteMapProvider
türetilen bir sınıf oluşturmayı içerir.
Bu öğreticide, uygulama mimarisinden kaynaklanan ürün ve kategori bilgilerine göre site haritasını temel alan özel bir site haritası sağlayıcısının nasıl oluşturulacağını gördük. Sağlayıcımız sınıfı genişletti StaticSiteMapProvider
ve verileri alan, site haritası hiyerarşisini oluşturan ve sonuçta elde edilen yapıyı sınıf düzeyinde bir değişkende önbelleğe alan bir yöntem oluşturmayı BuildSiteMap
gerektiriyordu. Temel alınan Categories
veya Products
veriler değiştirildiğinde önbelleğe alınan yapıyı geçersiz kılma amacıyla bir geri çağırma işleviyle sql önbellek bağımlılığı kullandık.
Mutlu Programlama!
Daha Fazla Bilgi
Bu öğreticide ele alınan konular hakkında daha fazla bilgi için aşağıdaki kaynaklara bakın:
- Site Haritalarını SQL Server'da ve Beklediğiniz SQL Site Haritası Sağlayıcısında Depolama
- Sağlayıcı Araç Seti
- ASP.NET 2.0'ın Gelişmiş Site Gezinti Özellikleri
Yazar hakkında
Yedi ASP/ASP.NET kitabının yazarı ve 4GuysFromRolla.com kurucusu Scott Mitchell, 1998'den beri Microsoft Web teknolojileriyle çalışmaktadır. Scott bağımsız bir danışman, eğitmen ve yazar olarak çalışır. Son kitabı Sams Teach Yourself ASP.NET 24 Hours 2.0'dır. Ona adresinden mitchell@4GuysFromRolla.comulaşabilirsiniz.
Özel Teşekkürler
Bu öğretici serisi birçok yararlı gözden geçiren tarafından gözden geçirildi. Bu öğreticinin baş gözden geçirenleri Dave Gardner, Zack Jones, Teresa Murphy ve Bernadette Leigh oldu. Yaklaşan MSDN makalelerimi gözden geçirmek istiyor musunuz? Öyleyse, mitchell@4GuysFromRolla.com'a bir mesaj bırakın.