将多个模型传递给 _Layout.cshtml,其中一个创建动态菜单

Passing multiple models to _Layout.cshtml and one of them creates a dynamic menu

请对我温柔一点。我已经阅读了该网站上其他人的大量 MVC 答案,但现在我找不到答案。

通过课程,我创建了一个 LoginDetails 项目,其中一个视图模型从控制器传递到每个视图,一切正常。
型号(来自 SQL table):

namespace MVC_EFCRUD.Models
{
    using System;
    using System.Collections.Generic;

    public partial class LoginDetail
    {
        public int UserId { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
    }
}

这是控制器的一个片段:

using MVC_EFCRUD.Models;

namespace MVC_EFCRUD.Controllers
{
    public class LoginDetailsController : Controller
    {
        private tapnamron_PNMEntities db = new tapnamron_PNMEntities();

        // GET: LoginDetails
        public ActionResult Index()
        {
            return View(db.LoginDetails.ToList());
        }

这是来自 LoginDetails 文件夹的索引视图的片段:

@model IEnumerable<MVC_EFCRUD.Models.LoginDetail>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.UserName)
        </th>

之后,我决定创建一个菜单,该菜单是从 SQL table 动态构建的,然后将视图模型传递给 _Layout.cshtml,因此菜单将出现在所有页面上。为了不让这段代码与有效的 LoginDetails 代码混淆,我创建了一个名为 SiteMenu 的新文件夹,其中包含一个索引页面,我将在其中测试菜单。 它也很好用,但后来我决定重新 运行 LoginDetails 部分只是为了确保 仍然有效并得到以下错误:

Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type System.Collections.Generic.List 1[MVC_EFCRUD.Models.LoginDetail], but this dictionary requires a model item of type System.Collections.Generic.IEnumerable 1[MVC_EFCRUD.Models.uspGetSiteMenu_Results].

所以,从我在这个网站上读到的内容(非常感谢)我认为,因为我将视图模型传递到我有菜单的 _Layout 页面,所以这与 LoginDetails 页面上的模型冲突,因为它们'我们都访问同一个 _Layout 页面。 这是 SiteMenu 模型(使用存储过程检索):

namespace MVC_EFCRUD.Models
{
    using System;

    public partial class uspGetSiteMenu_Results
    {
        public int MenuID { get; set; }
        public int ParentID { get; set; }
        public string SiteName { get; set; }
        public string Url { get; set; }
        public string Target { get; set; }
    }
}

来自控制器的代码段:

namespace MVC_EFCRUD.Controllers
{
    public class SiteMenuController : Controller
    {
        // GET: SiteMenu
        public ActionResult Index()
        {

            tapnamron_PNMSiteMenu ent = new tapnamron_PNMSiteMenu();

            return View(ent.GetSiteMenu().ToList());
        }
    }
}

来自 _Layout.cshtml 的片段:

@model IEnumerable<MVC_EFCRUD.Models.uspGetSiteMenu_Results>

<!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>

    <link href="~/Content/bootstrap.min.css" rel="stylesheet" type="text/css" />
    <script src="~/Scripts/modernizr-2.6.2.js"></script>
  <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
  @using System.Web.Optimization;
  @Styles.Render("~/Content/css")
</head>
<body>

  <nav class="navbar navbar-inverse navbar-static-top marginBottom-0" role="navigation">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
    </div>

    <div class="collapse navbar-collapse" id="navbar-collapse-1">
      <ul class="nav navbar-nav">
@foreach (var item in Model.Where(m => m.ParentID == 0))
{
  switch (item.SiteName)
  {

我尝试了几种方法来解决这个问题,但没有成功。 这是我在 HomeController 中的一堆修改后的代码:

namespace MVC_EFCRUD.Controllers
{    
    public class HomeController : Controller
    {
    //tapnamron_PNMEntities m1 = new tapnamron_PNMEntities();
    //tapnamron_PNMSiteMenu m2 = new tapnamron_PNMSiteMenu();

    //LoginDetail modelLoginDetail = new LoginDetail();
    //uspGetSiteMenu_Results ModeluspGetSiteMenu_Results = new uspGetSiteMenu_Results();

    // GET: Home
    public ActionResult Index()
        {
      //List<object> model = new List<object>();
      //model.Add(modelLoginDetail);
      //model.Add(ModeluspGetSiteMenu_Results);

      //var model = Tuple.Create<IEnumerable<string>, IEnumerable<string>>(LoginDetail, uspGetSiteMenu_Results);
      //return View(model);

      //var viewmodel = new MyModels
      //{
      //  uspGetSiteMenu_Results = uspGetSiteMenu_Results,
      //  LoginDetail = LoginDetail
      //};

      //var myModels = new MyModels();

          return View();
        }
    }
}

我在 _Layout 中也有这个但删除了它以防它给你造成混淆(它是从 HomeController 传递的):

@*@model IEnumerable<object>

@{ 
  List<MVC_EFCRUD.Models.LoginDetail> lstLoginDetails = Model.ToList()[0]
    as List<MVC_EFCRUD.Models.LoginDetail>;

  List<MVC_EFCRUD.Models.uspGetSiteMenu_Results> lstSiteMenu = Model.ToList()[1]
    as List<MVC_EFCRUD.Models.uspGetSiteMenu_Results>;
}*@

我意识到我真的不想将多个模型添加到布局页面,因为站点将有更多与登录详细信息无关的内容,但当然会在每个页面上使用菜单。我想可能会有一些部分视图或嵌套视图,但据我发现,仍然会有冲突。

所以,我想要的是一种使用多个模型并仍然在每个页面上包含动态创建的菜单(也是使用模型构建的)的方法?

非常感谢!

你采取了错误的方法。不要将模型传递给布局,而是在布局页面中使用 Html.RenderAction() 生成菜单。用 [ChildActionOnly] 属性标记 SiteMenuController 控制器的 Index() 方法(这样它就不能直接从浏览器调用)并将其更改为 return 您的部分视图菜单。

然后在_Layout.cshtml文件中,使用

@{ Html.RenderAction("Index", "SiteMenu"); }

在布局页面输出菜单

_Layout.cshtml(没有@model

<head>
    ....
</head>
<body>
    @{ Html.RenderAction("Index", "SiteMenu"); }
    @RenderBody()
</body>

/Views/SiteMenu/Index.cshtml(部分视图包含与菜单相关的所有 html)

@model IEnumerable<MVC_EFCRUD.Models.uspGetSiteMenu_Results>
<nav class="navbar navbar-inverse navbar-static-top marginBottom-0" role="navigation">
    ....
    @foreach (var item in Model.Where(m => m.ParentID == 0))
    {
        ....
    }
</nav>

控制器

[ChildActionOnly]
public ActionResult Index()
{
    tapnamron_PNMSiteMenu ent = new tapnamron_PNMSiteMenu();
    return PartialView(ent.GetSiteMenu().ToList()); // .ToList() not necessary as the view is IEnemerable<T>
}