其實我沒有那麼喜歡寫程式

【ASP.NET MVC】使用MvcSiteMapProvider的DynamicNodeProvider遇到的問題(1)

要實做網站breadcrumbm的功能,用MvcSiteMapProvider可以很方便完成,你可以在SiteMap這檔案定義階層關係

<mvcSiteMapNode title="唱片分類" action="Genre" controller="Store">
    <mvcSiteMapNode title="唱片列表" action="Album"  />
</mvcSiteMapNode>

結果會長的像這樣

首頁 > 唱片分類 > 唱片列表

不過這可能無法滿足很多人的需求,你可能希望唱片分類這節點能依照目前的位置改變名稱
例如

首頁 > Metal > 唱片列表

首頁 > Punk > 唱片列表

這時候就要實做 DynamicNodeProvider
首先你必須先繼承DynamicNodeProviderBase
然後覆寫public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)

CODE:
  • dynamicNode.Title = genre.Name;為要顯示的名稱
  • dynamicNode.RouteValues.Add("genreId",genre.Id)代表遇到該route值顯示該節點
GenreDynamicNodeProvider.cs
public class GenreDynamicNodeProvider:DynamicNodeProviderBase
{
    GenreRepository repository = GetRepository();

    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
    {
        foreach (var genre in repository.All())
        {
            var dynamicNode = new DynamicNode();
            dynamicNode.Title = genre.Name;
            dynamicNode.RouteValues.Add("genreId",genre.Id);
            yield return dynamicNode;
        }

    }
}
SiteMap:

在SiteMap的部分如下定義
加上dynamicNodeProvider="DynamicProviderNode位置, 組件名稱"

<mvcSiteMapNode title="唱片分類" action="Genre" controller="Store" dynamicNodeProvider="Music.GenreDynamicNodeProvider, Music">
    <mvcSiteMapNode title="唱片列表" action="Album" />
</mvcSiteMapNode>

你可能會想要建立多個階層的動態節點(畢竟音樂類型百百種....)

例如

首頁 > Metal > Black Metal > 唱片列表

首頁 > Metal > Pop Metal > 唱片列表

首頁 > Punk > Emo > 唱片列表

首頁 > Punk > Pop Punk > 唱片列表

你可能和我一樣一開始會這樣寫

Route:
 routes.MapRoute(
     name: "AlbumGenre",
     url: "Home/Genre/{genreId}",
     defaults: new { area = "Manage", controller = "Home", action = "Genre" }
  );
  
 routes.MapRoute(
     name: "AlbumSubGenre",
     url: "Home/Genre/{genreId}/SubGenre/{subGrenreId}",
     defaults: new { area = "Manage", controller = "Home", action = "SubGenre" }
  );
CODE:

記得要為他們的階層關系定義Key和ParentKey,否則他們並不會知道彼此的關係!

主分類
GenreDynamicNodeProvider.cs
public class GenreDynamicNodeProvider:DynamicNodeProviderBase
{
    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
    {
        GenreRepository repository = GetRepository();
        foreach (var genre in repository.All())
        {
            var dynamicNode = new DynamicNode();
            dynamicNode.Title = genre.Name;
            dynamicNode.RouteValues.Add("genreId",genre.Id);
            dynamicNode.Key="genre_"+genre.Id;
            yield return dynamicNode;
        }

    }
}
次分類
SubGenreDynamicNodeProvider.cs
public class SubGenreDynamicNodeProvider:DynamicNodeProviderBase
{
    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
    {
        GenreRepository repository = GetRepository();
        foreach (var subGenre in repository.All())
        {
            var dynamicNode = new DynamicNode();
            dynamicNode.Title = subGenre.Name;
            dynamicNode.RouteValues.Add("subGenreId",subGenre.Id);
            dynamicNode.ParentKey="genre_"+subGenre.Genre.Id;
            yield return dynamicNode;
        }

    }
}
SiteMap:
<mvcSiteMapNode title="唱片分類" action="Genre" controller="Store" dynamicNodeProvider="Music.GenreDynamicNodeProvider, Music">
    <mvcSiteMapNode title="唱片次分類" action="SubGenre" controller="Store" dynamicNodeProvider="Music.SubGenreDynamicNodeProvider, Music">
        <mvcSiteMapNode title="唱片列表" action="Album" />
    </mvcSiteMapNode>
</mvcSiteMapNode>

我原本也是這樣寫,不過我在運行網頁的時後發現變得很慢,最後發現原來這種寫法會將每個子節點都複製到父節點上,而不是只將真正的子節點複製給他真正的父親
例如

Metal
  • Black Metal
  • Pop Metal
  • Emo
  • Pop Punk
Punk
  • Black Metal
  • Pop Metal
  • Emo
  • Pop Punk

如果節點數很多的話........就暴了
(在真實世界裡如果你把Emo和Pop Punk歸類到Metal可是會被金屬頭追殺的)
其實我們只需要一個DynamicNodeProvider物件就好了,就像在寫資料結構的LinkList一樣,寫法如下

CODE:
GenreDynamicNodeProvider.cs
public class GenreDynamicNodeProvider:DynamicNodeProviderBase
{
    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
    {
        GenreRepository repository = GetRepository();
        
        foreach (var genre in repository.All())
        {
            var genreNode = new DynamicNode();
            genreNode.Title = genre.Name;
            genreNode.RouteValues.Add("genreId",genre.Id);
            genreNode.Key="genre_"+genre.Id;
            yield return genreNode;
            
            foreach(var subGenre in genre.SubGenre)
            {
                var subGenreNode = new DynamicNode();
                subGenreNode.Title = subGenre.Name;
                subGenreNode.RouteValues.Add("subGenreId",subGenre.Id);
                subGenreNode.RouteValues.Add("genreId",genre.Id);
                subGenreNode.ParentKey="genre_"+genre.Id;
                yield return subGenreNode;                    
            }
        }

    }
}
SiteMap:
<mvcSiteMapNode title="唱片分類" action="Genre" controller="Store" dynamicNodeProvider="Music.GenreDynamicNodeProvider, Music">
    <mvcSiteMapNode title="唱片列表" action="Album" />
</mvcSiteMapNode>

其實還有一堆問題,過幾天補上>"<