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

时间:2016-03-11 04:42:25

标签: c# asp.net-mvc asp.net-mvc-viewmodel

请对我温柔。我已经在这个网站上为其他人阅读了大量的MVC答案,但现在我找到了一些我无法找到答案的答案。

通过课程我创建了一个LoginDetails项目,其中一个viewmodel从控制器传递到每个视图,一切都很顺利。
模型(来自SQL表):

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; }
    }
}

以下是Controller的摘录:

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表动态构建的菜单,然后将viewmodel传递给_Layout.cshtml,这样菜单就会出现在所有页面上。为了不使用工作的LoginDetails代码使这个代码混乱,我创建了一个名为SiteMenu的新文件夹,其中包含一个索引页面,我将测试该菜单。 它也很好,但后来我决定重新运行LoginDetails部分,以确保仍然有用并得到以下错误:

  

异常详细信息:System.InvalidOperationException:模型项   传入字典的类型   System.Collections.Generic.List 1 [MVC_EFCRUD.Models.LoginDetail],   但是这个字典需要一个类型的模型项   System.Collections.Generic.IEnumerable 1 [MVC_EFCRUD.Models.uspGetSiteMenu_Results]。

所以,从我在这个网站上看到的内容(非常感谢)我想,因为我将一个viewmodel传递给我的菜单的_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; }
    }
}
来自Controller的

代码段:

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>;
}*@

我意识到我真的不想在Layout页面中添加多个模型,因为与LoginDetails无关的网站会有更多东西,但当然会在每个页面上使用菜单。我想可能是一些部分视图或嵌套视图,但从我发现的仍然会有冲突。

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

非常感谢!

1 个答案:

答案 0 :(得分:1)

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

然后在_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>
}