사용자 지정 데이터베이스 중심 사이트 맵 공급자 빌드(C#)
로 스콧 미첼
ASP.NET 2.0의 기본 사이트 맵 공급자는 정적 XML 파일에서 해당 데이터를 검색합니다. XML 기반 공급자는 많은 중소 규모의 웹 사이트에 적합하지만 대규모 웹 애플리케이션에는 보다 동적 사이트 맵이 필요합니다. 이 자습서에서는 비즈니스 논리 계층에서 데이터를 검색하는 사용자 지정 사이트 맵 공급자를 빌드하여 데이터베이스에서 데이터를 검색합니다.
소개
ASP.NET 2.0의 사이트 맵 기능을 사용하면 페이지 개발자가 XML 파일과 같은 일부 영구 매체에서 웹 애플리케이션의 사이트 맵을 정의할 수 있습니다. 정의되면 사이트 맵 데이터는 네임스페이스의 클래스 System.Web
를 통해 SiteMap
프로그래밍 방식으로 또는 SiteMapPath, 메뉴 및 TreeView 컨트롤과 같은 다양한 탐색 웹 컨트롤을 통해 액세스할 수 있습니다. 사이트 맵 시스템은 서로 다른 사이트 맵 serialization 구현을 만들고 웹 애플리케이션에 연결할 수 있도록 공급자 모델을 사용합니다. ASP.NET 2.0과 함께 제공되는 기본 사이트 맵 공급자는 XML 파일에 사이트 맵 구조를 유지합니다. 마스터 페이지 및 사이트 탐색 자습서로 돌아가서 이 구조가 포함된 파일을 Web.sitemap
만들고 각 새 자습서 섹션으로 XML을 업데이트했습니다.
이러한 자습서와 같이 사이트 맵의 구조가 상당히 정적이면 기본 XML 기반 사이트 맵 공급자가 제대로 작동합니다. 그러나 많은 시나리오에서는 보다 동적 사이트 맵이 필요합니다. 각 범주와 제품이 웹 사이트 구조의 섹션으로 표시되는 그림 1에 표시된 사이트 맵을 고려합니다. 이 사이트 맵을 사용하면 루트 노드에 해당하는 웹 페이지를 방문하면 모든 범주가 나열될 수 있는 반면, 특정 범주의 웹 페이지를 방문하면 해당 범주의 제품이 나열되고 특정 제품의 웹 페이지를 보면 해당 제품의 세부 정보가 표시됩니다.
그림 1: 사이트 맵 구조의 범주 및 제품 구성(전체 크기 이미지를 보려면 클릭)
이 범주 및 제품 기반 구조는 파일에 하드 코딩 Web.sitemap
될 수 있지만 범주 또는 제품이 추가, 제거 또는 이름을 바꿀 때마다 파일을 업데이트해야 합니다. 따라서 데이터베이스 또는 애플리케이션 아키텍처의 비즈니스 논리 계층에서 구조를 검색한 경우 사이트 맵 유지 관리가 크게 간소화됩니다. 이렇게 하면 제품 및 범주가 추가, 이름 변경 또는 삭제되면 사이트 맵이 이러한 변경 내용을 반영하도록 자동으로 업데이트됩니다.
ASP.NET 2.0의 사이트 맵 직렬화는 공급자 모델 위에 빌드되므로 데이터베이스 또는 아키텍처와 같은 대체 데이터 저장소에서 해당 데이터를 캡처하는 자체 사용자 지정 사이트 맵 공급자를 만들 수 있습니다. 이 자습서에서는 BLL에서 해당 데이터를 검색하는 사용자 지정 공급자를 빌드합니다. 시작해 보겠습니다!
참고 항목
이 자습서에서 만든 사용자 지정 사이트 맵 공급자는 애플리케이션의 아키텍처 및 데이터 모델과 긴밀하게 결합되어 있습니다. Jeff Prosise의 SQL Server 에 사이트 맵 저장 및 문서를 기다리 던 SQL 사이트 맵 공급자는 SQL Server에 사이트 맵 데이터를 저장하는 일반화된 접근 방식을 검토합니다.
1단계: 사용자 지정 사이트 맵 공급자 웹 페이지 만들기
사용자 지정 사이트 맵 공급자 만들기를 시작하기 전에 먼저 이 자습서에 필요한 ASP.NET 페이지를 추가해 보겠습니다. 먼저 .라는 SiteMapProvider
새 폴더를 추가합니다. 다음으로, 다음 ASP.NET 페이지를 해당 폴더에 추가하여 각 페이지를 마스터 페이지와 Site.master
연결해야 합니다.
Default.aspx
ProductsByCategory.aspx
ProductDetails.aspx
또한 폴더에 CustomProviders
하위 폴더를 추가합니다 App_Code
.
그림 2: 사이트 맵 공급자 관련 자습서에 대한 ASP.NET 페이지 추가
이 섹션에 대한 자습서는 하나뿐이므로 섹션의 자습서를 나열할 필요가 Default.aspx
없습니다. 대신 GridView Default.aspx
컨트롤에 범주를 표시합니다. 2단계에서 이 문제를 해결하겠습니다.
다음으로, 페이지에 대한 참조를 포함하도록 업데이트 Web.sitemap
합니다 Default.aspx
. 특히 캐싱 후 다음 태그를 추가합니다 <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." />
업데이트 Web.sitemap
한 후 잠시 브라우저를 통해 자습서 웹 사이트를 봅니다. 왼쪽 메뉴에는 이제 유일한 사이트 맵 공급자 자습서에 대한 항목이 포함됩니다.
그림 3: 이제 사이트 맵 공급자 자습서에 대한 항목이 사이트 맵에 포함됩니다.
이 자습서의 주요 초점은 사용자 지정 사이트 맵 공급자를 만들고 해당 공급자를 사용하도록 웹 애플리케이션을 구성하는 방법을 보여 주는 것입니다. 특히 그림 1에 표시된 것처럼 각 범주 및 제품에 대한 노드와 함께 루트 노드를 포함하는 사이트 맵을 반환하는 공급자를 빌드합니다. 일반적으로 사이트 맵의 각 노드는 URL을 지정할 수 있습니다. 사이트 맵의 경우 루트 노드의 URL은 ~/SiteMapProvider/Default.aspx
데이터베이스의 모든 범주를 나열하는 URL입니다. 사이트 맵의 각 범주 노드에는 지정된 categoryID의 모든 제품을 나열하는 URL~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
이 있습니다. 마지막으로 각 제품 사이트 맵 노드는 특정 제품의 세부 정보를 표시하는 가리킵니 ~/SiteMapProvider/ProductDetails.aspx?ProductID=productID
다.
시작하려면 , ProductsByCategory.aspx
및 ProductDetails.aspx
페이지를 만들어야 Default.aspx
합니다. 이러한 페이지는 각각 2단계, 3단계 및 4단계에서 완료됩니다. 이 자습서의 추력은 사이트 맵 공급자에 있으므로 이전 자습서에서는 이러한 종류의 다중 페이지 마스터/세부 보고서 만들기를 다루었으므로 2~4단계를 서둘러 진행합니다. 여러 페이지에 걸쳐 있는 마스터/세부 보고서를 만드는 방법에 대한 새로 고침이 필요한 경우 두 페이지에 걸친 마스터/세부 정보 필터링 자습서를 다시 참조하세요 .
2단계: 범주 목록 표시
폴더에서 Default.aspx
페이지를 열고 도구 상자에서 디자이너로 GridView를 끌어서 설정합니다 ID
Categories
.SiteMapProvider
GridView의 스마트 태그에서 명명된 CategoriesDataSource
새 ObjectDataSource에 바인딩하고 클래스의 GetCategories
메서드를 사용하여 CategoriesBLL
데이터를 검색하도록 구성합니다. 이 GridView는 범주만 표시하고 데이터 수정 기능을 제공하지 않으므로 UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정합니다.
그림 4: 메서드를 사용하여 GetCategories
범주를 반환하도록 ObjectDataSource 구성(전체 크기 이미지를 보려면 클릭)
그림 5: UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정합니다(전체 크기 이미지를 보려면 클릭).
데이터 원본 구성 마법사를 완료한 후 Visual Studio는 , CategoryName
Description
, NumberOfProducts
및 BrochurePath
.에 대한 BoundField를 CategoryID
추가합니다. BoundFields만 포함되도록 GridView를 CategoryName
Description
편집하고 BoundField 속성을 HeaderText
범주로 업데이트 CategoryName
합니다.
다음으로, HyperLinkField를 추가하고 가장 왼쪽 필드가 되도록 배치합니다. DataNavigateUrlFields
속성을 CategoryID
로 설정하고 DataNavigateUrlFormatString
속성을 ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}
로 설정합니다. Text
속성을 제품 보기로 설정합니다.
그림 6: GridView에 Categories
HyperLinkField 추가
ObjectDataSource를 만들고 GridView의 필드를 사용자 지정한 후 두 컨트롤 선언적 태그는 다음과 같이 표시됩니다.
<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>
그림 7은 브라우저를 통해 볼 때를 보여 Default.aspx
줍니다. 범주의 제품 보기 링크를 ProductsByCategory.aspx?CategoryID=categoryID
클릭하면 3단계에서 빌드됩니다.
그림 7: 각 범주가 제품 보기 링크와 함께 나열됩니다(전체 크기 이미지를 보려면 클릭).
3단계: 선택한 범주 제품 나열
ProductsByCategory.aspx
페이지를 열고 GridView를 추가하여 이름을 지정합니다ProductsByCategory
. 스마트 태그에서 GridView를 새 ObjectDataSource에 ProductsByCategoryDataSource
바인딩합니다. 클래스의 GetProductsByCategoryID(categoryID)
메서드를 사용하도록 ProductsBLL
ObjectDataSource를 구성하고 드롭다운 목록을 UPDATE, INSERT 및 DELETE 탭에서 (없음)으로 설정합니다.
그림 8: 클래스 메서드 GetProductsByCategoryID(categoryID)
사용ProductsBLL
(전체 크기 이미지를 보려면 클릭)
데이터 원본 구성 마법사의 마지막 단계에서는 categoryID에 대한 매개 변수 원본을 묻는 메시지를 표시합니다. 이 정보는 쿼리 문자열 필드를 CategoryID
통해 전달되므로 드롭다운 목록에서 QueryString을 선택하고 그림 9에 표시된 대로 QueryStringField 텍스트 상자에 CategoryID를 입력합니다. 마침을 클릭하여 마법사를 완료합니다.
그림 9: categoryID 매개 변수에 쿼리 문자열 필드 사용CategoryID
(전체 크기 이미지를 보려면 클릭)
마법사를 완료한 후 Visual Studio는 제품 데이터 필드의 GridView에 해당 BoundFields 및 CheckBoxField를 추가합니다. , UnitPrice
및 SupplierName
BoundFields를 ProductName
제외한 모든 것을 제거합니다. 이러한 세 개의 BoundFields HeaderText
속성을 사용자 지정하여 제품, 가격 및 공급업체를 각각 읽습니다. BoundField를 UnitPrice
통화 형식으로 지정합니다.
다음으로, HyperLinkField를 추가하고 가장 왼쪽 위치로 이동합니다. 해당 Text
속성을 보기 세부 정보, 해당 속성을 DataNavigateUrlFields
로 설정하고 해당 속성을 DataNavigateUrlFormatString
로 ~/SiteMapProvider/ProductDetails.aspx?ProductID={0}
설정합니다.
그림 10: 가리키는 보기 세부 정보 하이퍼링크 필드 추가 ProductDetails.aspx
이러한 사용자 지정을 수행한 후 GridView 및 ObjectDataSource의 선언적 태그는 다음과 유사합니다.
<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>
브라우저를 통해 보기 Default.aspx
로 돌아가서 음료에 대한 제품 보기 링크를 클릭합니다. 이렇게 하면 음료 범주에 ProductsByCategory.aspx?CategoryID=1
속하는 Northwind 데이터베이스에 있는 제품의 이름, 가격 및 공급업체를 표시합니다(그림 11 참조). 사용자를 범주 목록 페이지(Default.aspx
)로 되돌리는 링크와 선택한 범주의 이름과 설명을 표시하는 DetailsView 또는 FormView 컨트롤을 포함하도록 이 페이지를 더욱 개선할 수 있습니다.
그림 11: 음료 이름, 가격 및 공급업체가 표시됩니다(전체 크기 이미지를 보려면 클릭).
4단계: 제품 세부 정보 표시
마지막 페이지인 ProductDetails.aspx
에는 선택한 제품 세부 정보가 표시됩니다. DetailsView를 열고 ProductDetails.aspx
도구 상자에서 디자이너로 끕니다. DetailsView의 ID
속성을 설정하여 ProductInfo
해당 Height
값과 Width
속성 값을 지웁다. 스마트 태그에서 DetailsView를 새 ObjectDataSource에 ProductDataSource
바인딩하여 ObjectDataSource를 구성하여 클래스의 GetProductByProductID(productID)
메서드에서 ProductsBLL
해당 데이터를 끌어옵니다. 2단계와 3단계에서 만든 이전 웹 페이지와 마찬가지로 UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정합니다.
그림 12: 메서드를 사용하도록 GetProductByProductID(productID)
ObjectDataSource 구성(전체 크기 이미지를 보려면 클릭)
데이터 원본 구성 마법사의 마지막 단계에서 productID 매개 변수의 원본을 묻는 메시지가 표시됩니다. 이 데이터는 쿼리 문자열 필드를 ProductID
통해 제공되므로 드롭다운 목록을 QueryString으로 설정하고 QueryStringField 텍스트 상자를 ProductID로 설정합니다. 마지막으로 마침 단추를 클릭하여 마법사를 완료합니다.
그림 13: querystring 필드에서 해당 값을 ProductID
끌어오도록 productID 매개 변수 구성(전체 크기 이미지를 보려면 클릭)
데이터 원본 구성 마법사를 완료한 후 Visual Studio는 제품 데이터 필드에 대한 DetailsView에 해당 BoundFields 및 CheckBoxField를 만듭니다. ProductID
, SupplierID
및 CategoryID
BoundFields를 제거하고 원하는 대로 나머지 필드를 구성합니다. 몇 가지 미적 구성 후 내 DetailsView 및 ObjectDataSource의 선언적 태그는 다음과 같습니다.
<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>
이 페이지를 테스트하려면 음료 범주로 돌아가 Default.aspx
서 제품 보기를 클릭합니다. 음료 제품 목록에서 차이 차의 세부 정보 보기 링크를 클릭합니다. 이렇게 하면 ProductDetails.aspx?ProductID=1
차이차의 세부 정보가 표시됩니다(그림 14 참조).
그림 14: Chai Tea의 공급업체, 범주, 가격 및 기타 정보가 표시됩니다(전체 크기 이미지를 보려면 클릭).
5단계: 사이트 맵 공급자의 내부 작업 이해
사이트 맵은 웹 서버 메모리에 계층 구조를 형성하는 인스턴스의 SiteMapNode
컬렉션으로 표시됩니다. 정확히 하나의 루트가 있어야 하고, 루트가 아닌 모든 노드에는 정확히 하나의 부모 노드가 있어야 하며, 모든 노드에는 임의의 자식 수가 있을 수 있습니다. 각 SiteMapNode
개체는 웹 사이트 구조의 섹션을 나타내며, 이러한 섹션에는 일반적으로 해당 웹 페이지가 있습니다. 따라서 클래스에는 SiteMapNode
나타내는 섹션에 대한 정보를 제공하는 , Url
및 Description
같은 속성Title
이 SiteMapNode
있습니다. 또한 계층 구조에서 각각 SiteMapNode
을 고유하게 식별하는 속성과 이 계층ParentNode
ChildNodes
, , NextSibling
PreviousSibling
등을 설정하는 데 사용되는 속성도 Key
있습니다.
그림 15는 그림 1의 일반 사이트 맵 구조를 보여 주지만 구현 세부 정보를 더 자세히 스케치합니다.
그림 15: 각각 SiteMapNode
에는 등과 같은 Title
Url
Key
속성이 있습니다(전체 크기 이미지를 보려면 클릭).
사이트 맵은 네임스페이스의 클래스를 System.Web
SiteMap
통해 액세스할 수 있습니다. 이 클래스의 RootNode
속성은 사이트 맵의 루트 SiteMapNode
인스턴스를 반환합니다CurrentNode
. 현재 요청된 Url
페이지의 URL과 일치하는 속성을 반환 SiteMapNode
합니다. 이 클래스는 ASP.NET 2.0의 탐색 웹 컨트롤에서 내부적으로 사용됩니다.
클래스의 속성에 SiteMap
액세스 하는 경우 사이트 맵 구조를 일부 영구 매체에서 메모리로 직렬화 해야 합니다. 그러나 사이트 맵 serialization 논리는 클래스에 SiteMap
하드 코딩되지 않습니다. 대신 런타임에 클래스는 SiteMap
serialization에 사용할 사이트 맵 공급자 를 결정합니다. 기본적으로 클래스는 XmlSiteMapProvider
올바르게 서식이 지정된 XML 파일에서 사이트 맵의 구조를 읽는 데 사용됩니다. 그러나 약간의 작업을 통해 사용자 지정 사이트 맵 공급자를 만들 수 있습니다.
모든 사이트 맵 공급자는 클래스에서 SiteMapProvider
파생되어야 합니다. 여기에는 사이트 맵 공급자에 필요한 필수 메서드 및 속성이 포함되지만 많은 구현 세부 정보는 생략됩니다. 두 번째 클래스는 StaticSiteMapProvider
클래스를 SiteMapProvider
확장하고 필요한 기능의 보다 강력한 구현을 포함합니다. 내부적으로 사이트 StaticSiteMapProvider
맵의 인스턴스를 a Hashtable
에 저장 SiteMapNode
하고, Clear()
RemoveNode(siteMapNode),
내부 맵에 s를 추가하고 제거하는 SiteMapNode
등의 AddNode(child, parent)
메서드를 Hashtable
제공합니다. XmlSiteMapProvider
는 StaticSiteMapProvider
에서 파생됩니다.
확장하는 사용자 지정 사이트 맵 공급자를 StaticSiteMapProvider
만들 때 재정 BuildSiteMap
의해야 하는 두 가지 추상 메서드가 있습니다 GetRootNodeCore
. BuildSiteMap
이름에서 알 수 있듯이 영구 스토리지에서 사이트 맵 구조를 로드하고 메모리에 생성해야 합니다. GetRootNodeCore
는 사이트 맵의 루트 노드를 반환합니다.
웹 애플리케이션에서 사이트 맵 공급자를 사용하려면 먼저 애플리케이션 구성에 등록해야 합니다. 기본적으로 클래스는 XmlSiteMapProvider
이름을 AspNetXmlSiteMapProvider
사용하여 등록됩니다. 추가 사이트 맵 공급자를 등록하려면 다음 태그를 추가합니다 Web.config
.
<configuration>
<system.web>
...
<siteMap defaultProvider="defaultProviderName">
<providers>
<add name="name" type="type" />
</providers>
</siteMap>
</system.web>
</configuration>
이름 값은 사람이 읽을 수 있는 이름을 공급자에 할당하고 형식은 사이트 맵 공급자의 정규화된 형식 이름을 지정합니다. 사용자 지정 사이트 맵 공급자를 만든 후 7단계에서 이름 및 형식 값에 대한 구체적인 값을 살펴보겠습니다.
사이트 맵 공급자 클래스는 클래스에서 SiteMap
처음 액세스할 때 인스턴스화되고 웹 애플리케이션의 수명 동안 메모리에 유지됩니다. 여러 동시 웹 사이트 방문자로부터 호출될 수 있는 사이트 맵 공급자의 인스턴스는 하나뿐이므로 공급자의 메서드는 스레드로부터 안전해야 합니다.
성능 및 확장성을 위해 메모리 내 사이트 맵 구조를 캐시하고 메서드가 호출될 때마다 BuildSiteMap
다시 만드는 대신 캐시된 구조를 반환하는 것이 중요합니다. BuildSiteMap
는 페이지에서 사용 중인 탐색 컨트롤과 사이트 맵 구조의 깊이에 따라 사용자당 페이지 요청당 여러 번 호출될 수 있습니다. 어쨌든 사이트 맵 구조를 BuildSiteMap
캐시하지 않으면 호출될 때마다 아키텍처에서 제품 및 범주 정보를 다시 검색해야 합니다(데이터베이스에 대한 쿼리가 발생). 이전 캐싱 자습서에서 설명한 것처럼 캐시된 데이터는 부실해질 수 있습니다. 이를 방지하기 위해 시간 또는 SQL 캐시 종속성 기반 만료를 사용할 수 있습니다.
참고 항목
사이트 맵 공급자는 필요에 따라 메서드를 재정의할 Initialize
수 있습니다. Initialize
는 사이트 맵 공급자가 처음 인스턴스화되고 다음과 같은 <add name="name" type="type" customAttribute="value" />
요소의 Web.config
<add>
공급자에 할당된 모든 사용자 지정 특성을 전달할 때 호출됩니다. 페이지 개발자가 공급자 코드를 수정하지 않고도 다양한 사이트 맵 공급자 관련 설정을 지정할 수 있도록 허용하려는 경우에 유용합니다. 예를 들어 아키텍처를 통해서가 아니라 데이터베이스에서 직접 범주 및 제품 데이터를 읽는 경우 페이지 개발자가 공급자 코드에서 하드 코딩된 값을 사용하는 대신 데이터베이스 연결 문자열 Web.config
지정할 수 있도록 할 수 있습니다. 6단계에서 빌드할 사용자 지정 사이트 맵 공급자는 이 Initialize
메서드를 재정의하지 않습니다. 이 메서드를 사용하는 Initialize
예제는 SQL Server 문서에서 Jeff Prosise의 사이트 맵 저장 문서를 참조하세요.
6단계: 사용자 지정 사이트 맵 공급자 만들기
Northwind 데이터베이스의 범주 및 제품에서 사이트 맵을 빌드하는 사용자 지정 사이트 맵 공급자를 만들려면 확장하는 클래스를 만들어야 합니다 StaticSiteMapProvider
. 1단계에서 폴더에 폴더를 추가 CustomProviders
하도록 요청했습니다. 이 폴더NorthwindSiteMapProvider
에 App_Code
새 클래스를 추가합니다. 다음 코드를 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?;
}
}
}
문으로 시작하는lock
이 클래스의 BuildSiteMap
메서드 탐색부터 시작해 보겠습니다. 이 문은 lock
한 번에 하나의 스레드만 입력할 수 있으므로 해당 코드에 대한 액세스를 직렬화하고 두 개의 동시 스레드가 서로의 발가락을 밟지 못하게 합니다.
클래스 수준 SiteMapNode
변수 root
는 사이트 맵 구조를 캐시하는 데 사용됩니다. 사이트 맵이 처음으로 생성되거나 기본 데이터가 수정 root
된 후 처음으로 생성되면 null
사이트 맵 구조가 생성됩니다. 이 메서드가 다음에 호출될 때는 이 메서드가 호출 root
되지 않도록 사이트 맵의 루트 노드가 생성 프로세스 중에 할당 root
됩니다null
. 따라서 사이트 맵 구조가 아닌 null
한 root
다시 만들 필요 없이 호출자에게 반환됩니다.
루트인 null
경우 사이트 맵 구조는 제품 및 범주 정보에서 만들어집니다. 사이트 맵은 인스턴스를 SiteMapNode
만든 다음 클래스 메서드 AddNode
호출을 통해 계층 구조를 형성하여 StaticSiteMapProvider
빌드됩니다. AddNode
는 내부 부기 작업을 수행하여 다양한 SiteMapNode
인스턴스를 저장 Hashtable
합니다. 계층 구조 생성을 시작하기 전에 먼저 내부 Hashtable
요소에서 요소를 지우는 메서드를 호출 Clear
합니다. 다음으로, ProductsBLL
클래스의 GetProducts
메서드와 결과 ProductsDataTable
변수가 지역 변수에 저장됩니다.
사이트 맵 생성은 루트 노드를 만들고 할당하여 root
시작합니다. 여기와 전체 BuildSiteMap
에서 SiteMapNode
사용되는 생성자의 오버로드는 다음 정보를 전달합니다.
- 사이트 맵 공급자(
this
)에 대한 참조입니다. - s
Key
.SiteMapNode
이 필수 값은 각각SiteMapNode
에 대해 고유해야 합니다. - s
Url
.SiteMapNode
Url
는 선택 사항이지만 제공된 경우 각SiteMapNode
값은Url
고유해야 합니다. - 필요한
SiteMapNode
sTitle
입니다.
메서드 호출은 AddNode(root)
사이트 맵에 루트로 추가합니다 SiteMapNode
root
. 다음으로, 각 ProductRow
열 ProductsDataTable
거형입니다. 현재 제품 범주에 대한 a SiteMapNode
가 이미 있는 경우 참조됩니다. 그렇지 않으면 범주에 대한 새 SiteMapNode
항목이 만들어지고 메서드 호출을 SiteMapNode``root
통해 AddNode(categoryNode, root)
자식으로 추가됩니다. 적절한 범주 SiteMapNode
노드를 찾거나 만든 SiteMapNode
후에는 현재 제품에 대해 생성되고 을 통해 AddNode(productNode, categoryNode)
범주 SiteMapNode
의 자식으로 추가됩니다. 범주 SiteMapNode
의 Url
속성 값은 ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID=categoryID
제품 Url
SiteMapNode
속성이 할당된 ~/SiteMapNode/ProductDetails.aspx?ProductID=productID
동안입니다.
참고 항목
데이터베이스 값 CategoryID
이 있는 제품은 해당 속성이 NULL
None Url
으로 설정되고 속성이 빈 문자열로 설정된 범주 SiteMapNode
Title
아래에 그룹화됩니다. 클래스 메서드 GetProductsByCategory(categoryID)
에 현재 값이 있는 제품만 반환할 수 있는 기능이 없기 때문에 ProductBLL
빈 문자열로 NULL
CategoryID
설정 Url
하기로 결정했습니다. 또한 탐색 컨트롤 SiteMapNode
이 속성에 대한 Url
값이 없는 렌더링 방법을 보여주고 싶었습니다. None SiteMapNode
Url
속성이 가리키 ProductsByCategory.aspx
도록 이 자습서를 확장하지만 값이 있는 NULL
CategoryID
제품만 표시하는 것이 좋습니다.
사이트 맵을 생성한 후에는 개체를 통해 AggregateCacheDependency
테이블 및 Products
테이블에 대한 SQL 캐시 종속성을 사용하여 임의의 개체가 Categories
데이터 캐시에 추가됩니다. 이전 자습서 에서는 SQL 캐시 종속성을 사용하여 SQL 캐시 종속성을 살펴보기 시작했습니다. 그러나 사용자 지정 사이트 맵 공급자는 아직 탐색하지 않은 데이터 캐시 메서드의 Insert
오버로드를 사용합니다. 이 오버로드는 개체가 캐시에서 제거될 때 호출되는 대리자를 최종 입력 매개 변수로 허용합니다. 특히 클래스에서 추가로 정의된 메서드를 OnSiteMapChanged
가리키는 새CacheItemRemovedCallback
대리자를 NorthwindSiteMapProvider
전달합니다.
참고 항목
사이트 맵의 메모리 내 표현은 클래스 수준 변수 root
를 통해 캐시됩니다. 사용자 지정 사이트 맵 공급자 클래스의 인스턴스는 하나뿐이므로 해당 인스턴스는 웹 애플리케이션의 모든 스레드 간에 공유되므로 이 클래스 변수는 캐시 역할을 합니다. 또한 이 메서드는 BuildSiteMap
데이터 캐시를 사용하지만, 테이블의 Products
기본 데이터베이스 데이터가 Categories
변경될 때 알림을 받는 수단으로만 사용됩니다. 데이터 캐시에 배치된 값은 현재 날짜와 시간에 불과합니다. 실제 사이트 맵 데이터는 데이터 캐시에 배치되지 않습니다 .
이 메서드는 BuildSiteMap
사이트 맵의 루트 노드를 반환하여 완료됩니다.
나머지 메서드는 매우 간단합니다. GetRootNodeCore
는 루트 노드를 반환하는 역할을 담당합니다. BuildSiteMap
루트 GetRootNodeCore
를 반환하므로 반환 값만 반환 BuildSiteMap
합니다. 메서드는 OnSiteMapChanged
캐시 항목이 제거될 때로 다시 null
설정합니다root
. 루트를 다시 설정하면 null
다음에 호출될 때 BuildSiteMap
사이트 맵 구조가 다시 작성됩니다. 마지막으로, 이러한 값이 CachedDate
있는 경우 속성은 데이터 캐시에 저장된 날짜 및 시간 값을 반환합니다. 이 속성은 페이지 개발자가 사이트 맵 데이터가 마지막으로 캐시된 시기를 결정하는 데 사용할 수 있습니다.
7단계: 등록NorthwindSiteMapProvider
웹 애플리케이션에서 6단계에서 만든 사이트 맵 공급자를 사용 NorthwindSiteMapProvider
하려면 웹 애플리케이션을 섹션Web.config
에 <siteMap>
등록해야 합니다. 특히 요소 내에 다음 태그를 <system.web>
추가합니다.Web.config
<siteMap defaultProvider="AspNetXmlSiteMapProvider">
<providers>
<add name="Northwind" type="NorthwindSiteMapProvider" />
</providers>
</siteMap>
이 태그는 두 가지 작업을 수행합니다. 첫째, 기본 제공 AspNetXmlSiteMapProvider
이 기본 사이트 맵 공급자임을 나타냅니다. 둘째, 6단계에서 만든 사용자 지정 사이트 맵 공급자를 사람 친화적인 이름 Northwind로 등록합니다.
참고 항목
애플리케이션 폴더에 있는 사이트 맵 공급자의 App_Code
경우 특성 값 type
은 단순히 클래스 이름입니다. 또는 컴파일된 어셈블리가 웹 애플리케이션 /Bin
디렉터리에 배치된 별도의 클래스 라이브러리 프로젝트에서 사용자 지정 사이트 맵 공급자를 만들 수 있습니다. 이 경우 특성 값은 type
네임스페이스입니다.ClassName, AssemblyName .
업데이트 Web.config
한 후 잠시 시간을 내어 브라우저의 자습서에서 페이지를 봅니다. 왼쪽의 탐색 인터페이스는 여전히 에 정의된 Web.sitemap
섹션과 자습서를 보여 줍니다. 이는 기본 공급자로 남겨 AspNetXmlSiteMapProvider
두기 때문입니다. 이를 사용하는 NorthwindSiteMapProvider
탐색 사용자 인터페이스 요소를 만들려면 Northwind 사이트 맵 공급자를 사용하도록 명시적으로 지정해야 합니다. 8단계에서 이 작업을 수행하는 방법을 살펴보겠습니다.
8단계: 사용자 지정 사이트 맵 공급자를 사용하여 사이트 맵 정보 표시
사용자 지정 사이트 맵 공급자를 만들고 등록Web.config
하면 폴더의 페이지 및 ProductDetails.aspx
탐색 컨트롤에 탐색 컨트롤Default.aspx
ProductsByCategory.aspx
을 추가할 준비가 된 것입니다SiteMapProvider
. 먼저 페이지를 열고 Default.aspx
도구 상자에서 디자이너로 끌어 SiteMapPath
옵니다. SiteMapPath 컨트롤은 도구 상자의 탐색 섹션에 있습니다.
그림 16: SiteMapPath 추가 Default.aspx
(전체 크기 이미지를 보려면 클릭)
SiteMapPath 컨트롤은 사이트 맵 내의 현재 페이지 위치를 나타내는 이동 경로를 표시합니다. 마스터 페이지 및 사이트 탐색 자습서에서 다시 마스터 페이지의 맨 위에 SiteMapPath를 추가했습니다.
잠시 브라우저를 통해 이 페이지를 봅니다. 그림 16에 추가된 SiteMapPath는 기본 사이트 맵 공급자를 사용하여 해당 데이터를 가져옵니다 Web.sitemap
. 따라서 이동 경로 경로는 오른쪽 위 모서리에 있는 이동 경로와 마찬가지로 사이트 맵을 사용자 지정하는 홈 > 을 표시합니다.
그림 17: 이동 경로가 기본 사이트 맵 공급자를 사용합니다(전체 크기 이미지를 보려면 클릭).
그림 16에서 SiteMapPath를 추가하려면 6단계에서 만든 사용자 지정 사이트 맵 공급자를 사용하고 해당 SiteMapProvider
속성을 In에 Web.config
할당한 이름인 Northwind로 NorthwindSiteMapProvider
설정합니다. 아쉽게도 디자이너는 기본 사이트 맵 공급자를 계속 사용하지만, 이 속성을 변경한 후 브라우저를 통해 페이지를 방문하면 이동 경로가 이제 사용자 지정 사이트 맵 공급자를 사용하는 것을 볼 수 있습니다.
그림 18: 이제 이동 경로에서 사용자 지정 사이트 맵 공급자 NorthwindSiteMapProvider
를 사용합니다(전체 크기 이미지를 보려면 클릭).
SiteMapPath 컨트롤은 더 기능적인 사용자 인터페이스 ProductsByCategory.aspx
를 페이지와 ProductDetails.aspx
페이지에 표시합니다. 이러한 페이지에 SiteMapPath를 추가하여 두 페이지의 속성을 Northwind로 설정합니다 SiteMapProvider
. Default.aspx
음료 제품 보기 링크를 클릭한 다음 차이차의 세부 정보 보기 링크를 클릭합니다. 그림 19에서 볼 수 있듯이 이동 경로에는 현재 사이트 맵 섹션(차이 차)과 상위 항목인 음료 및 모든 범주가 포함됩니다.
그림 19: 이제 이동 경로에서 사용자 지정 사이트 맵 공급자 NorthwindSiteMapProvider
를 사용합니다(전체 크기 이미지를 보려면 클릭).
메뉴 및 TreeView 컨트롤과 같은 SiteMapPath 외에도 다른 탐색 사용자 인터페이스 요소를 사용할 수 있습니다. Default.aspx
예를 들어 이 자습서의 다운로드에 있는 , ProductsByCategory.aspx
및 ProductDetails.aspx
페이지는 모두 메뉴 컨트롤을 포함합니다(그림 20 참조). ASP.NET 2.0의 탐색 컨트롤 및 사이트 맵 시스템에 대한 자세한 내용은 ASP.NET 2.0의 정교한 사이트 탐색 기능 및 ASP.NET 2.0 빠른 시작의 사이트 탐색 컨트롤 사용 섹션을 참조하세요.
그림 20: 메뉴 컨트롤은 각 범주 및 제품을 나열합니다(전체 크기 이미지를 보려면 클릭).
이 자습서의 앞부분에서 설명한 것처럼 사이트 맵 구조는 클래스를 통해 SiteMap
프로그래밍 방식으로 액세스할 수 있습니다. 다음 코드는 기본 공급자의 루트 SiteMapNode
를 반환합니다.
SiteMapNode root = SiteMap.RootNode;
애플리케이션의 AspNetXmlSiteMapProvider
기본 공급자이므로 위의 코드는 에 정의된 Web.sitemap
루트 노드를 반환합니다. 기본값이 아닌 사이트 맵 공급자를 참조하려면 다음과 같이 클래스의 Providers
속성을 사용합니다SiteMap
.
SiteMapNode root = SiteMap.Providers["name"].RootNode;
여기서 이름은 사용자 지정 사이트 맵 공급자(웹 애플리케이션의 경우 Northwind)의 이름입니다.
사이트 맵 공급자와 관련된 멤버에 액세스하려면 공급자 인스턴스를 검색한 다음 적절한 형식으로 캐스팅하는 데 사용합니다 SiteMap.Providers["name"]
. 예를 들어 ASP.NET 페이지에 s CachedDate
속성을 표시 NorthwindSiteMapProvider
하려면 다음 코드를 사용합니다.
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!";
}
참고 항목
SQL 캐시 종속성 기능을 테스트해야 합니다. 및 ProductsByCategory.aspx
페이지를 방문한 Default.aspx
후 편집, 삽입 및 ProductDetails.aspx
삭제 섹션의 자습서 중 하나로 이동하여 범주 또는 제품의 이름을 편집합니다. 그런 다음 폴더의 페이지 SiteMapProvider
중 하나로 돌아갑니다. 폴링 메커니즘이 기본 데이터베이스의 변경 내용을 기록할 수 있는 충분한 시간이 경과했다고 가정하면 새 제품 또는 범주 이름을 표시하도록 사이트 맵을 업데이트해야 합니다.
요약
ASP.NET 2.0의 사이트 맵 기능에는 클래스, 여러 기본 제공 탐색 웹 컨트롤 및 XML 파일에 유지되는 사이트 맵 정보를 예상하는 기본 사이트 맵 공급자가 포함 SiteMap
됩니다. 데이터베이스, 애플리케이션 아키텍처 또는 원격 웹 서비스와 같은 다른 원본의 사이트 맵 정보를 사용하려면 사용자 지정 사이트 맵 공급자를 만들어야 합니다. 여기에는 클래스에서 직접 또는 간접적으로 파생되는 클래스를 만드는 작업이 SiteMapProvider
포함됩니다.
이 자습서에서는 애플리케이션 아키텍처에서 선별된 제품 및 범주 정보에 대한 사이트 맵을 기반으로 하는 사용자 지정 사이트 맵 공급자를 만드는 방법을 알아보았습니다. 공급자는 클래스를 StaticSiteMapProvider
확장하여 데이터를 검색하고, 사이트 맵 계층 구조를 생성하고, 결과 구조를 클래스 수준 변수에 캐시하는 메서드를 만들어야 BuildSiteMap
했습니다. 기본 또는 Products
데이터가 수정될 때 Categories
캐시된 구조를 무효화하기 위해 콜백 함수와 함께 SQL 캐시 종속성을 사용했습니다.
행복한 프로그래밍!
추가 정보
이 자습서에서 설명하는 항목에 대한 자세한 내용은 다음 리소스를 참조하세요.
작성자 정보
7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술을 연구해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 샘스 티치 자신 ASP.NET 24 시간에 2.0입니다. 그는 에서 mitchell@4GuysFromRolla.com찾을 http://ScottOnWriting.NET수있는 자신의 블로그를 통해 도달 할 수 있습니다 .
특별 감사
이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 데이브 가드너, 잭 존스, 테레사 머피, 버나데트 리였습니다. 예정된 MSDN 문서를 검토하는 데 관심이 있으신가요? 그렇다면 선을 놓습니다 mitchell@4GuysFromRolla.com.