モデル定義(Models/Category.cs と Models/ReferencePage.cs)
// Models/Category.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace YourApp.Models
{
public class Category
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
// ナビゲーションプロパティ
public virtual ICollection<ReferencePage> ReferencePages { get; set; } = new List<ReferencePage>();
}
}
// Models/ReferencePage.cs
using System.ComponentModel.DataAnnotations;
namespace YourApp.Models
{
public class ReferencePage
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Url { get; set; }
// 外部キー
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
2. DbContext 定義(Data/ApplicationDbContext.cs)
using Microsoft.EntityFrameworkCore;
using YourApp.Models;
namespace YourApp.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Category> Categories { get; set; }
public DbSet<ReferencePage> ReferencePages { get; set; }
}
}
3. コントローラー(Controllers/MenuController.cs)
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using YourApp.Data;
using System.Threading.Tasks;
using System.Linq;
namespace YourApp.Controllers
{
public class MenuController : Controller
{
private readonly ApplicationDbContext _context;
public MenuController(ApplicationDbContext context)
{
_context = context;
}
// 部分ビューとして呼び出す
public async Task<IActionResult> CategoryMenu()
{
var categories = await _context.Categories
.Include(c => c.ReferencePages)
.OrderBy(c => c.Name)
.ToListAsync();
return PartialView("_CategoryMenu", categories);
}
}
}
4. 部分ビュー(Views/Menu/_CategoryMenu.cshtml)
@model IEnumerable<YourApp.Models.Category>
<!-- Bootstrap 5 Accordion Menu -->
<div class="accordion" id="categoryAccordion">
@foreach (var category in Model)
{
var collapseId = $"collapse{category.Id}";
<div class="accordion-item">
<h2 class="accordion-header" id="heading@category.Id">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#@collapseId" aria-expanded="false" aria-controls="@collapseId">
@category.Name
</button>
</h2>
<div id="@collapseId" class="accordion-collapse collapse" aria-labelledby="heading@category.Id" data-bs-parent="#categoryAccordion">
<div class="accordion-body p-0">
<ul class="list-group list-group-flush">
@foreach (var page in category.ReferencePages.OrderBy(p => p.Title))
{
<li class="list-group-item">
<a href="@page.Url" class="text-decoration-none">@page.Title</a>
</li>
}
</ul>
</div>
</div>
</div>
}
</div>
5. レイアウト(Views/Shared/_Layout.cshtml)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"] - YourApp</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- 左カラム -->
<nav class="col-md-3 col-lg-2 bg-light p-3">
@await Html.PartialAsync("_CategoryMenu", await Component.InvokeAsync("Menu", "CategoryMenu"))
<!-- または Html.Action("CategoryMenu", "Menu") (MVC5) -->
</nav>
<!-- メインコンテンツ -->
<main class="col-md-9 col-lg-10 p-4">
@RenderBody()
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
6. Program.cs(ASP.NET Core 6+)
using Microsoft.EntityFrameworkCore;
using YourApp.Data;
var builder = WebApplication.CreateBuilder(args);
// DbContext 登録(例: SQLite)
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
7. appsettings.json(SQLite の例)
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=app.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
✅ この構成で dotnet ef migrations add InitialCreate → dotnet ef database update を実行すれば、
DB が作成され、カテゴリーとページを登録すれば左カラムに動的メニューが表示されます。
もし希望があれば、このコードに 初期データ Seed 処理 を追加して、
起動時にサンプルカテゴリーとページが自動登録されるようにできますが、追加しますか?
そうすればすぐに動作確認できます。
Powered by Froala Editor