- PVSM.RU - https://www.pvsm.ru -

Практическое использование MvcSiteMapProvider в ASP.net MVC

Практическое использование MvcSiteMapProvider в ASP.net MVC

В данной статье показано использование MvcSiteMapProvider для построения динамического меню, как с его помощью сделать карту сайта и «breadcrumbs».

Практику использвания MvcSiteMapProvider я буду показывать на учебном проекте MVC Music Store [1].

Структура сайта следующая
Home
Store
   Rock
      The Best Of Men At Work The Best Of Men At Work
      For Those About To Rock We Salute You
   Classical
   Jazz
   …
Admin

Исходники MvcSiteMapProvider лежат на Github [2], лицензия MS-PL License.
Установка в проект с помощью nuget (Вид->Другие окна->Консоль диспетчера пакетов)

PM>Install-Package MvcSiteMapProvider

После установки этого пакета в папке Views/Shared/DisplayTemplates появятся новые view.
Практическое использование MvcSiteMapProvider в ASP.net MVC
Не нужные *.ascx файлы я удалил.
Также в корне проекта появятся Mvc.sitemap и MvcSiteMapSchema.xsd.
В web.config добавятся строки:

<siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
      <providers>
        <clear />
        <add name="MvcSiteMapProvider" 
             type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider" 
             siteMapFile="~/Mvc.Sitemap" 
             securityTrimmingEnabled="true" 
             cacheDuration="5" 
             enableLocalization="true" 
             scanAssembliesForSiteMapNodes="true" 
             includeAssembliesForScan="" 
             excludeAssembliesForScan="" 
             attributesToIgnore="visibility" 
             nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider" 
             controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider" 
             actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider" 
             aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider" 
             siteMapNodeUrlResolver="MvcSiteMapProvider.DefaultSiteMapNodeUrlResolver, MvcSiteMapProvider" 
             siteMapNodeVisibilityProvider="MvcSiteMapProvider.DefaultSiteMapNodeVisibilityProvider, MvcSiteMapProvider" 
             siteMapProviderEventHandler="MvcSiteMapProvider.DefaultSiteMapProviderEventHandler, MvcSiteMapProvider" />
      </providers>
    </siteMap>
</code>

Для начала нас интересует Mvc.sitemap, по умолчанию он имеет вид:

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0"
            xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0 MvcSiteMapSchema.xsd"
            enableLocalization="true">
  <mvcSiteMapNode title="Home" controller="Home" action="Index">
    <mvcSiteMapNode title="About" controller="Home" action="About"/>
  </mvcSiteMapNode>
</mvcSiteMap>

Xml-элемент mvcSiteMapNode — основной элемент карты сайта. Его основные атрибуты:

  • title — имя элемента, отображается в браузере
  • controller — контролёр, из маршрута
  • action — экшн, из маршрута
  • area — область (area), из маршрута
  • dynamicNodeProvider — класс, который генерирует набор mvcSiteMap, вместо этой ноды
  • visibility — видимость ноды, только карта сайта или только меню или другие варианты

Я добавил в него ноды соответствующие структуре сайта:

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0"
            xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0 MvcSiteMapSchema.xsd"
            enableLocalization="true">
  <mvcSiteMapNode title="Home" controller="Home" action="Index">
    <mvcSiteMapNode title="Store" controller="Store" action="Index">
    </mvcSiteMapNode>
    <mvcSiteMapNode title="Cart" controller="ShoppingCart" action="Index" />
    <mvcSiteMapNode title="Admin" controller="StoreManager" action="Index" >
    </mvcSiteMapNode>
  </mvcSiteMapNode>
</mvcSiteMap>

Здесь добавлены только 4 основных страницы. Теперь необходимо добавить жанры и альбомы в карту сайта:

  • Rock
    • The Best Of Men At Work The Best Of Men At Work
    • For Those About To Rock We Salute You
    • ...

  • Classical
  • Jazz
  • ...

Т.к. сайт у нас динамический, то альбомы и жанры будут постоянно меняться и статичная карта сайта не подойдёт. Для решения этой проблемы в MvcSiteMapProvider есть dynamicNodeProvider. Итак, создадим класс унаследованный от DynamicNodeProviderBase и переопределим метод GetDynamicNodeCollection, данные будут браться из бд:

public class StoreDynamicNodeProvider : DynamicNodeProviderBase
    {
        public MusicStoreEntities _db = new MusicStoreEntities();
        public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
        {
            var nodes = new List<DynamicNode>();
            var items = _db.Genres.ToList();
            foreach (var item in items)
            {
                DynamicNode node = new DynamicNode();
                // ключ должен быть уникальным для каждой ноды
                node.Key = "store_" + item.GenreId.ToString();
                node.RouteValues.Add("genre", item.Name);
                node.Action = "Browse";
                node.Controller = "Store";
                node.Title = item.Name;
                nodes.Add(node);
                if (item.Albums!=null)
                {
                    foreach (var item2 in item.Albums)
                    {
                        DynamicNode node2 = new DynamicNode();
                        node2.Key = "album_" + item2.AlbumId.ToString();
                        node2.ParentKey = node.Key;
                        node2.RouteValues.Add("id", item2.AlbumId);
                        node2.Action = "Details";
                        node2.Controller = "Store";
                        node2.Title = item2.Title;
                        nodes.Add(node2);
                    }
                }
            }
            return nodes;
        }
    }

Теперь необходимо в Mvc.sitemap поменять ноду Store:

<mvcSiteMapNode title="Store" controller="Store" action="Index">
    <mvcSiteMapNode title="" controller="Store" action="Index" dynamicNodeProvider="MvcMusicStore.Infrastructure.StoreDynamicNodeProvider, MvcMusicStore"/>
</mvcSiteMapNode>

Здесь главное изменение — добавление атрибута dynamicNodeProvider, в котором указывается полный путь к классу-генератору динамических нод.

Добавим breadcrumbs в шаблон сайта и отрисовку меню
В файл Views/Shared/_Layout.cshtml добавим:

    <div style="text-align: center;">
        Вы здесь: @Html.MvcSiteMap().SiteMapPath()
    </div>
    @Html.MvcSiteMap().Menu(2, 1)

Теперь сделаем выделение активного пункта в левом меню, для этого надо изменить шаблон Views/Shared/DisplayTemplates/MenuHelperModel.cshtml

<ul id="menu">
    @foreach (var node in Model.Nodes) { 
        <li style="@(node.IsInCurrentPath && !node.IsRootNode ? "text-decoration: underline;" : "")">
            <a href="@node.Url">@node.Title</a>
        </li>
    }
</ul>

Получится примерно следующее:
Практическое использование MvcSiteMapProvider в ASP.net MVC

Переходим к админке.
В админке я хочу, чтобы отображался только путь на сайте, а меню слева — нет.
Структура следующая:

  • Admin
    • Редактирование альбома Facelift
    • Просмотр альбома Facelift
    • Удаление альбома Facelift
    • и так для каждого альбома

Аналогично для раздела Store, добавим класс унаследованный от DynamicNodeProviderBase.

public class AdminDynamicNodeProvider : DynamicNodeProviderBase
    {
        public MusicStoreEntities _db = new MusicStoreEntities();
        public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
        {
            var nodes = new List<DynamicNode>();
            var items = _db.Albums.ToList();
            foreach (var item in items)
            {
                var node=new DynamicNode() { 
                    Key="admin_album_edit_"+ item.AlbumId.ToString(),
                    Action="Edit",
                    Controller="StoreManager",
                    Title="Редактирование альбома "+item.Title
                    };
                node.RouteValues.Add("id", item.AlbumId);
                nodes.Add(node);

                node = new DynamicNode()
                {
                    Key = "admin_album_delete_" + item.AlbumId.ToString(),
                    Action = "Delete",
                    Controller = "StoreManager",
                    Title = "Удаление альбома " + item.Title
                };
                node.RouteValues.Add("id", item.AlbumId);
                nodes.Add(node);

                node = new DynamicNode()
                {
                    Key = "admin_album_details_" + item.AlbumId.ToString(),
                    Action = "Details",
                    Controller = "StoreManager",
                    Title = "Просмотр альбома " + item.Title
                };
                node.RouteValues.Add("id", item.AlbumId);
                nodes.Add(node);
                
            }
            return nodes;
        }
    }

Аналогично изменим ноду Admin:

<mvcSiteMapNode title="Admin" controller="StoreManager" action="Index" >
  <mvcSiteMapNode title="" controller="StoreManager" action="Index" dynamicNodeProvider="MvcMusicStore.Infrastructure.AdminDynamicNodeProvider, MvcMusicStore"/>
  <mvcSiteMapNode title="Create" controller="StoreManager"  action="Create" />
</mvcSiteMapNode>

Получится следующее:
Практическое использование MvcSiteMapProvider в ASP.net MVC

Нам необходимо скрыть в меню все ненужные пункты, но путь на сайте должен отображаться корректно. Для этого в Mvc.sitemap в необходимых нодах добавляем атрибут visibility=«SiteMapPathHelper,!*», а чтобы он заработал важно в web.config в разделе где добавляется Mvc.sitemap поменять значение атрибута siteMapNodeVisibilityProvider на «MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider».
Окончательная версия файла Mvc.sitemap:

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0"
            xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0 MvcSiteMapSchema.xsd"
            enableLocalization="true">
  <mvcSiteMapNode title="Home" controller="Home" action="Index" visibility="">
    <mvcSiteMapNode title="Store" controller="Store" action="Index">
      <mvcSiteMapNode title="" controller="Store" action="Index" dynamicNodeProvider="MvcMusicStore.Infrastructure.StoreDynamicNodeProvider, MvcMusicStore"/>
    </mvcSiteMapNode>
    <mvcSiteMapNode title="Cart" controller="ShoppingCart" action="Index" />
    <mvcSiteMapNode title="Admin" controller="StoreManager" action="Index" >
      <mvcSiteMapNode title="" controller="StoreManager" action="Index" visibility="SiteMapPathHelper,!*" dynamicNodeProvider="MvcMusicStore.Infrastructure.AdminDynamicNodeProvider, MvcMusicStore"/>
      <mvcSiteMapNode title="Create" controller="StoreManager" visibility="SiteMapPathHelper,!*" action="Create" />
    </mvcSiteMapNode>
  </mvcSiteMapNode>
</mvcSiteMap>

Теперь всё отображается корректно:
Практическое использование MvcSiteMapProvider в ASP.net MVC

Вот, собственно, и всё.
Исходные коды примера можно скачать здесь [3]

Автор: slrzz

Источник [4]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/asp-net-mvc/33274

Ссылки в тексте:

[1] MVC Music Store: http://mvcmusicstore.codeplex.com/

[2] Github: https://github.com/maartenba/MvcSiteMapProvider

[3] здесь: http://slrz.ru/MvcMusicStore-w-SiteMap.zip

[4] Источник: http://habrahabr.ru/post/175431/