Управление иерархическими данными

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 04.08.2016

В этой статье мы рассмотрим управление иерархическими данными в Entity Framework 6 и использование их в ASP.NET MVC 5 на примере построения меню. Вообще меню представляет собой идеальную иерархическую структуру, где один пункт меню может содержать несколько подпунктов, а каждый из подпунктов, в свою очередь, также может содержать различные подпункты.

Вначале определим модель для пункта меню:

public class MenuItem
{
    public int Id { get; set; }
    public string Header { get; set; }	// заголовок меню
    public string Url { get; set; }	// адрес ссылки
    public int? Order { get; set; }  // порядок следования пункта в подменю
    public int? ParentId { get; set; }  // ссылка на id родительского меню
    public MenuItem Parent { get; set; }	// родительское меню

    public ICollection<MenuItem> Children { get; set; }	// дочерние пункты меню
    public MenuItem()
    {
        Children = new List<MenuItem>();
    }
}

Определим контекст данных:

public class ApplicationContext : DbContext
{
    public ApplicationContext() : base("DefaultConnection")
    {

    }
    public DbSet<MenuItem> MenuItems { get; set; }
}

Также определим класс инициализатора, который добавить некоторые начальные пункты меню:

public class AppDbInitializer : DropCreateDatabaseAlways<ApplicationContext>
{
    protected override void Seed(ApplicationContext db)
    {
		var menuItems = new List<MenuItem>
		{
			new MenuItem{Id=1, Header = "Главная", Url = "/Home/Index", Order = 1},
            new MenuItem{Id=2, Header = "О сайте", Url = "/Home/About", Order = 2},
            new MenuItem{Id=3, Header = "Контакты", Url = "/Home/Contact", Order = 3},
            new MenuItem{Id=4, Header = "Меню второго уровня 1", Url = "#", Order = 1, ParentId = 2},
            new MenuItem{Id=5, Header = "Меню второго уровня 2", Url = "#", Order = 2, ParentId = 2},
            new MenuItem{Id=6, Header = "Меню второго уровня 3", Url = "#", Order = 3, ParentId = 2},
            new MenuItem{Id=7, Header = "Меню третьго уровня 1", Url = "#",  Order = 1, ParentId = 4},
            new MenuItem{Id=8, Header = "Меню третьго уровня 2", Url = "#", Order = 2, ParentId = 4},
            new MenuItem{Id=9, Header = "Меню третьго уровня 3", Url = "#", Order = 3, ParentId = 4}
        };
        db.MenuItems.AddRange(menuItems);
        db.SaveChanges();
    }
}

Теперь мы можем использовать эти объекты в качестве меню в приложении. Для этого определим в контроллере дополнительный метод, который будет возвращать частичное представление с меню:

public class HomeController : Controller
{
    ApplicationContext db = new ApplicationContext();

    public ActionResult Menu()
    {
        List<MenuItem> menuItems = db.MenuItems.ToList();

        return PartialView(menuItems);
    }
	// остальные методы
}

И определим для метода само частичное представление Menu.cshtml:

@using MenuApp.Models
@model IEnumerable<MenuItem>

@helper BuildMenu(IEnumerable<MenuItem> data, int? parentId = null)
{
var items = data.Where(d => d.ParentId == parentId).OrderBy(i => i.Order);
if (items.Any())
{
            <ul>
                @foreach (var item in items)
                {
                    <li>
                        <a href="@item.Url">@item.Header</a>
                        @BuildMenu(data, item.Id)
                    </li>
                }
            </ul>
    }
}

<div id="cssmenu">
    @BuildMenu(Model)
</div>

Для создания меню применяется хелпер BuildMenu(), в котором из переданного списка получаем коллекцию объектов меню для определенного родительского id и при наличии в этой коллекции объектов выводим их в список. Затем рекурсивно вызываем хелпер BuildMenu для вложенных пунктов меню.

Поскольку элементы меню самого верхнего уровня имеют в качестве id родительского пункта меню значение null, то при вызове хелпера для второго параметра передается значение null в качестве значения по умолчанию.

И в конце где-нибудь на мастер-сранице _Layout.cshtml мы можем определить обращение к методу Menu:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    @Html.Action("Menu", "Home")
    <!-- остальное содержимое элемента body-->
</body>
</html>

В итоге получится вот такое меню, которое остается только стилизовать:

Иерархические данные в ASP.NET MVC 5 и Entity Framework 6
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850