You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在所有页面访问模型:布局页菜单数据传递方案咨询

Best Solutions for Global Menu with Entity Subitems in ASP.NET MVC

Great question—this is a super common scenario in ASP.NET MVC, and there are clean, maintainable ways to avoid code duplication without risking overwriting view parameters from your controllers. Let’s break down the top options:


1. Action Filters (My Top Pick for Global Menus)

Action filters let you inject logic before/after action methods run, which is ideal for adding global data like menu items. The key benefit here is you’re adding data to the view context, not replacing anything—so you won’t overwrite existing model or view data from your controllers.

Step-by-Step Implementation:

First, create a custom action filter to fetch and inject menu data:

public class MenuActionFilterAttribute : ActionFilterAttribute
{
    // Inject your data service (use dependency injection here)
    private readonly ILocationService _locationService;

    public MenuActionFilterAttribute(ILocationService locationService)
    {
        _locationService = locationService;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Only run for view results (skip API calls, redirects, etc.)
        if (filterContext.Result is ViewResult viewResult)
        {
            // Fetch your menu data
            var locations = _locationService.GetAllLocations();
            // Add to ViewData (use strongly-typed models if you prefer)
            viewResult.ViewData["LocationsMenu"] = locations;
        }

        base.OnActionExecuted(filterContext);
    }
}

Register it as a global filter in Startup.cs (or Global.asax for older MVC versions):

services.AddControllersWithViews(options =>
{
    options.Filters.Add<MenuActionFilterAttribute>();
});

Then render the menu in your _Layout.cshtml:

<ul class="main-menu">
    <li>
        Locations
        <ul class="submenu">
            @foreach (var location in ViewData["LocationsMenu"] as IEnumerable<Location>)
            {
                <li>@location.Name</li>
            }
        </ul>
    </li>
</ul>

2. Base Controller (Done Correctly)

If you already use a base controller for shared logic, you can safely override the View methods without overwriting existing parameters. The trick is to augment the view data before calling the base implementation, not replace it.

Step-by-Step Implementation:

Create your base controller with shared menu logic:

public class BaseController : Controller
{
    private readonly ILocationService _locationService;

    public BaseController(ILocationService locationService)
    {
        _locationService = locationService;
    }

    // Override all View method overloads to ensure menu data is added every time
    public override ViewResult View()
    {
        AddGlobalMenuData();
        return base.View();
    }

    public override ViewResult View(object model)
    {
        AddGlobalMenuData();
        return base.View(model);
    }

    public override ViewResult View(string viewName)
    {
        AddGlobalMenuData();
        return base.View(viewName);
    }

    public override ViewResult View(string viewName, object model)
    {
        AddGlobalMenuData();
        return base.View(viewName, model);
    }

    private void AddGlobalMenuData()
    {
        var locations = _locationService.GetAllLocations();
        ViewData["LocationsMenu"] = locations;
        // Add other global menu items here if needed
    }
}

Have all your controllers inherit from this base controller:

public class HomeController : BaseController
{
    public HomeController(ILocationService locationService) : base(locationService) {}

    public IActionResult Index()
    {
        // Your existing view model
        var homeViewModel = new HomeViewModel();
        // The base View method adds menu data without overwriting this model
        return View(homeViewModel);
    }
}

3. Child Action + Partial View

This approach keeps your menu logic fully decoupled from your main controllers. You create a dedicated controller for menu data, then call its action directly from your layout. It’s also easy to cache this action to reduce database hits.

Step-by-Step Implementation:

First, create a MenuController for menu-specific logic:

public class MenuController : Controller
{
    private readonly ILocationService _locationService;

    public MenuController(ILocationService locationService)
    {
        _locationService = locationService;
    }

    [ChildActionOnly] // Prevent direct access to this action
    [OutputCache(Duration = 3600)] // Cache menu data for 1 hour
    public ActionResult LocationsMenu()
    {
        var locations = _locationService.GetAllLocations();
        return PartialView("_LocationsMenu", locations);
    }
}

Create a partial view _LocationsMenu.cshtml:

@model IEnumerable<Location>

<ul class="submenu">
    @foreach (var location in Model)
    {
        <li>@location.Name</li>
    }
</ul>

Call the child action in your _Layout.cshtml:

<ul class="main-menu">
    <li>
        Locations
        @Html.Action("LocationsMenu", "Menu")
    </li>
</ul>

Quick Comparison of Options

ApproachBest ForProsCons
Action FiltersTruly global menus on every pageKeeps menu logic separate from controllersRequires filter registration
Base ControllerExisting base controller with shared logicSeamless for existing controller hierarchyMust override all View overloads
Child ActionCachable menus or partial-page menusFully decoupled, easy to testAdds a small overhead for child action execution

Key Best Practice

Whichever approach you choose, never directly write database queries in filters/controllers. Use dependency injection to inject a service layer (like ILocationService) that handles data access—this keeps your code testable and maintainable.

内容的提问来源于stack exchange,提问作者Lee Stevens

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:22:47