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

в 6:43, , рубрики: .net, ASP, asp.net mvc, метки:

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

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

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

Структура сайта следующая
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, лицензия 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

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

Автор: slrzz

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js