다음을 통해 공유


사용자 지정 데이터베이스 중심 사이트 맵 공급자 빌드(C#)

스콧 미첼

PDF 다운로드

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 .

사이트 맵 공급자 관련 자습서에 대한 ASP.NET 페이지 추가

그림 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.aspxProductDetails.aspx 페이지를 만들어야 Default.aspx합니다. 이러한 페이지는 각각 2단계, 3단계 및 4단계에서 완료됩니다. 이 자습서의 추력은 사이트 맵 공급자에 있으므로 이전 자습서에서는 이러한 종류의 다중 페이지 마스터/세부 보고서 만들기를 다루었으므로 2~4단계를 서둘러 진행합니다. 여러 페이지에 걸쳐 있는 마스터/세부 보고서를 만드는 방법에 대한 새로 고침이 필요한 경우 두 페이지에 걸친 마스터/세부 정보 필터링 자습서를 다시 참조하세요 .

2단계: 범주 목록 표시

폴더에서 Default.aspx 페이지를 열고 도구 상자에서 디자이너로 GridView를 끌어서 설정합니다 ID Categories.SiteMapProvider GridView의 스마트 태그에서 명명된 CategoriesDataSource 새 ObjectDataSource에 바인딩하고 클래스의 GetCategories 메서드를 사용하여 CategoriesBLL 데이터를 검색하도록 구성합니다. 이 GridView는 범주만 표시하고 데이터 수정 기능을 제공하지 않으므로 UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정합니다.

GetCategories 메서드를 사용하여 범주를 반환하도록 ObjectDataSource 구성

그림 4: 메서드를 사용하여 GetCategories 범주를 반환하도록 ObjectDataSource 구성(전체 크기 이미지를 보려면 클릭)

UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정

그림 5: UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정합니다(전체 크기 이미지를 보려면 클릭).

데이터 원본 구성 마법사를 완료한 후 Visual Studio는 , CategoryNameDescription, NumberOfProductsBrochurePath.에 대한 BoundField를 CategoryID추가합니다. BoundFields만 포함되도록 GridView를 CategoryName Description 편집하고 BoundField 속성을 HeaderText 범주로 업데이트 CategoryName 합니다.

다음으로, HyperLinkField를 추가하고 가장 왼쪽 필드가 되도록 배치합니다. DataNavigateUrlFields 속성을 CategoryID로 설정하고 DataNavigateUrlFormatString 속성을 ~/SiteMapProvider/ProductsByCategory.aspx?CategoryID={0}로 설정합니다. Text 속성을 제품 보기로 설정합니다.

Categories GridView에 HyperLinkField 추가

그림 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 탭에서 (없음)으로 설정합니다.

ProductsBLL 클래스의 GetProductsByCategoryID(categoryID) 메서드 사용

그림 8: 클래스 메서드 GetProductsByCategoryID(categoryID) 사용ProductsBLL(전체 크기 이미지를 보려면 클릭)

데이터 원본 구성 마법사의 마지막 단계에서는 categoryID에 대한 매개 변수 원본을 묻는 메시지를 표시합니다. 이 정보는 쿼리 문자열 필드를 CategoryID통해 전달되므로 드롭다운 목록에서 QueryString을 선택하고 그림 9에 표시된 대로 QueryStringField 텍스트 상자에 CategoryID를 입력합니다. 마침을 클릭하여 마법사를 완료합니다.

categoryID 매개 변수에 CategoryID 쿼리 문자열 필드 사용

그림 9: categoryID 매개 변수에 쿼리 문자열 필드 사용CategoryID(전체 크기 이미지를 보려면 클릭)

마법사를 완료한 후 Visual Studio는 제품 데이터 필드의 GridView에 해당 BoundFields 및 CheckBoxField를 추가합니다. , UnitPriceSupplierName BoundFields를 ProductName제외한 모든 것을 제거합니다. 이러한 세 개의 BoundFields HeaderText 속성을 사용자 지정하여 제품, 가격 및 공급업체를 각각 읽습니다. BoundField를 UnitPrice 통화 형식으로 지정합니다.

다음으로, HyperLinkField를 추가하고 가장 왼쪽 위치로 이동합니다. 해당 Text 속성을 보기 세부 정보, 해당 속성을 ProductID/>DataNavigateUrlFields로 설정하고 해당 속성을 DataNavigateUrlFormatString~/SiteMapProvider/ProductDetails.aspx?ProductID={0}설정합니다.

ProductDetails.aspx 가리키는 보기 세부 정보 HyperLinkField 추가

그림 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 탭의 드롭다운 목록을 (없음)으로 설정합니다.

GetProductByProductID(productID) 메서드를 사용하도록 ObjectDataSource 구성

그림 12: 메서드를 사용하도록 GetProductByProductID(productID) ObjectDataSource 구성(전체 크기 이미지를 보려면 클릭)

데이터 원본 구성 마법사의 마지막 단계에서 productID 매개 변수의 원본을 묻는 메시지가 표시됩니다. 이 데이터는 쿼리 문자열 필드를 ProductID통해 제공되므로 드롭다운 목록을 QueryString으로 설정하고 QueryStringField 텍스트 상자를 ProductID로 설정합니다. 마지막으로 마침 단추를 클릭하여 마법사를 완료합니다.

ProductID 쿼리 문자열 필드에서 해당 값을 끌어오도록 productID 매개 변수 구성

그림 13: querystring 필드에서 해당 값을 ProductID 끌어오도록 productID 매개 변수 구성(전체 크기 이미지를 보려면 클릭)

데이터 원본 구성 마법사를 완료한 후 Visual Studio는 제품 데이터 필드에 대한 DetailsView에 해당 BoundFields 및 CheckBoxField를 만듭니다. ProductID, SupplierIDCategoryID 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 참조).

Chai Tea의 공급업체, 범주, 가격 및 기타 정보가 표시됩니다.

그림 14: Chai Tea의 공급업체, 범주, 가격 및 기타 정보가 표시됩니다(전체 크기 이미지를 보려면 클릭).

5단계: 사이트 맵 공급자의 내부 작업 이해

사이트 맵은 웹 서버 메모리에 계층 구조를 형성하는 인스턴스의 SiteMapNode 컬렉션으로 표시됩니다. 정확히 하나의 루트가 있어야 하고, 루트가 아닌 모든 노드에는 정확히 하나의 부모 노드가 있어야 하며, 모든 노드에는 임의의 자식 수가 있을 수 있습니다. 각 SiteMapNode 개체는 웹 사이트 구조의 섹션을 나타내며, 이러한 섹션에는 일반적으로 해당 웹 페이지가 있습니다. 따라서 클래스에는 SiteMapNode 나타내는 섹션에 대한 정보를 제공하는 , UrlDescription같은 속성TitleSiteMapNode 있습니다. 또한 계층 구조에서 각각 SiteMapNode 을 고유하게 식별하는 속성과 이 계층ParentNodeChildNodes, , NextSiblingPreviousSibling등을 설정하는 데 사용되는 속성도 Key 있습니다.

그림 15는 그림 1의 일반 사이트 맵 구조를 보여 주지만 구현 세부 정보를 더 자세히 스케치합니다.

각 SiteMapNode에는 제목, URL, 키 등과 같은 속성이 있습니다.

그림 15: 각각 SiteMapNode 에는 등과 같은 TitleUrlKey속성이 있습니다(전체 크기 이미지를 보려면 클릭).

사이트 맵은 네임스페이스의 클래스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제공합니다. XmlSiteMapProviderStaticSiteMapProvider에서 파생됩니다.

확장하는 사용자 지정 사이트 맵 공급자를 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 하도록 요청했습니다. 이 폴더NorthwindSiteMapProviderApp_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. 따라서 사이트 맵 구조가 아닌 nullroot 다시 만들 필요 없이 호출자에게 반환됩니다.

루트인 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 s Title입니다.

메서드 호출은 AddNode(root) 사이트 맵에 루트로 추가합니다 SiteMapNode root . 다음으로, 각 ProductRowProductsDataTable 거형입니다. 현재 제품 범주에 대한 a SiteMapNode 가 이미 있는 경우 참조됩니다. 그렇지 않으면 범주에 대한 새 SiteMapNode 항목이 만들어지고 메서드 호출을 SiteMapNode``root 통해 AddNode(categoryNode, root) 자식으로 추가됩니다. 적절한 범주 SiteMapNode 노드를 찾거나 만든 SiteMapNode 후에는 현재 제품에 대해 생성되고 을 통해 AddNode(productNode, categoryNode)범주 SiteMapNode 의 자식으로 추가됩니다. 범주 SiteMapNodeUrl 속성 값은 ~/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.aspxProductsByCategory.aspx을 추가할 준비가 된 것입니다SiteMapProvider. 먼저 페이지를 열고 Default.aspx 도구 상자에서 디자이너로 끌어 SiteMapPath 옵니다. SiteMapPath 컨트롤은 도구 상자의 탐색 섹션에 있습니다.

siteMapPath를 Default.aspx 추가

그림 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.aspxProductDetails.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.