Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
В этой статье мы рассмотрим управление иерархическими данными в 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>
В итоге получится вот такое меню, которое остается только стилизовать: