如何在包含两个模型的ViewModel中使用@Html.DisplayNameFor?
MVC中Html.DisplayNameFor使用问题排查与解决
问题描述
作为MVC新手,无法正确在以下场景中使用Html.DisplayNameFor,已耗费2小时排查仍未解决,请求技术帮助。
Index.cshtml代码
@using MultipleModelInOneView; @model ViewModel @{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div class="main-content"> <div class="page-content"> <div class="container-fluid"> <div class="row"> <div class="col-12"> <div class="card"> <div class="card-body"> <h4 class="card-title">Customer Enquiry Products</h4> <p class="card-title-desc"></p> <!-- Button trigger modal --> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal"> Open Create Modal </button> <div id="mymodal" class="popup"> @Html.Partial("~/Views/Shared/_CustomerEnquiryCreate.cshtml", this.Model.CustomerEnquiry) </div> <table id="datatable-buttons" class="table table-striped table-bordered dt-responsive nowrap" style="border-collapse: collapse; border-spacing: 0; width: 100%;"> <thead> <tr> <th> @Html.DisplayNameFor(model => model.TargetPrice) </th> <th> @Html.DisplayNameFor(model => model.Quantity) </th> <th> @Html.DisplayNameFor(model => model.LeadTime) </th> <th> @Html.DisplayNameFor(model => model.Category) </th> <th> @Html.DisplayNameFor(model => model.RoHSStatus) </th> <th> @Html.DisplayNameFor(model => model.SalesRep) </th> <th> @Html.DisplayNameFor(model => model.PackageCase) </th> <th> @Html.DisplayNameFor(model => model.Description) </th> <th> @Html.DisplayNameFor(model => model.Currency.Currency1) </th> <th> @Html.DisplayNameFor(model => model.CustomerEnquiry.SalesRep) </th> <th> @Html.DisplayNameFor(model => model.Product.PartNumber) </th> <th> @Html.DisplayNameFor(model => model.RFQManufactuer.Name) </th> <th></th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.TargetPrice) </td> <td> @Html.DisplayFor(modelItem => item.Quantity) </td> <td> @Html.DisplayFor(modelItem => item.LeadTime) </td> <td> @Html.DisplayFor(modelItem => item.Category) </td> <td> @Html.DisplayFor(modelItem => item.RoHSStatus) </td> <td> @Html.DisplayFor(modelItem => item.SalesRep) </td> <td> @Html.DisplayFor(modelItem => item.PackageCase) </td> <td> @Html.DisplayFor(modelItem => item.Description) </td> <td> @Html.DisplayFor(modelItem => item.Currency.Currency1) </td> <td> @Html.DisplayFor(modelItem => item.CustomerEnquiry.SalesRep) </td> <td> @Html.DisplayFor(modelItem => item.Product.PartNumber) </td> <td> @Html.DisplayFor(modelItem => item.RFQManufactuer.Name) </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.EnqProdID }) | @Html.ActionLink("Details", "Details", new { id = item.EnqProdID }) | @Html.ActionLink("Delete", "Delete", new { id = item.EnqProdID }) </td> </tr> } </tbody> </table> </div> </div> </div> <!-- end col --> </div> <!-- end row --> </div> <!-- end main content--> </div> <!-- END layout-wrapper --> </div>
ViewModel代码
using REScrmNov22.Data; using REScrmNov22.Models; using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Web; namespace MultipleModelInOneView { public class ViewModel { public IEnumerable<CustEnquiryProduct> CustEnquiryProduct { get; set; } public IEnumerable<CustomerEnquiry> CustomerEnquiry { get; set; } } }
Index动作方法代码
public ActionResult Index() { var custEnquiryProducts = db.CustEnquiryProducts.Include(c => c.Currency).Include(c => c.CustomerEnquiry).Include(c => c.Product).Include(c => c.RFQManufactuer); var customerEnquiries = db.CustomerEnquiries.Include(c => c.Customer).Include(c => c.EnquiryStatu); ViewModel mymodel = new ViewModel(); mymodel.CustEnquiryProduct = custEnquiryProducts.ToList(); mymodel.CustomerEnquiry = customerEnquiries.ToList(); return View(mymodel.CustEnquiryProduct); }
问题根源
- 视图模型与传递的模型不匹配:视图声明的模型是
ViewModel,但控制器返回的是mymodel.CustEnquiryProduct(即IEnumerable<CustEnquiryProduct>),导致视图中model => model.TargetPrice这类表达式找不到对应属性——因为IEnumerable<CustEnquiryProduct>本身没有TargetPrice属性,只有集合里的单个元素才有。 Html.DisplayNameFor需要针对单个实体类型获取显示名称,而不是集合类型。
修复步骤
步骤1:修正控制器返回的模型
控制器应该返回完整的ViewModel实例,而不是其中的集合:
public ActionResult Index() { var custEnquiryProducts = db.CustEnquiryProducts.Include(c => c.Currency).Include(c => c.CustomerEnquiry).Include(c => c.Product).Include(c => c.RFQManufactuer); var customerEnquiries = db.CustomerEnquiries.Include(c => c.Customer).Include(c => c.EnquiryStatu); ViewModel mymodel = new ViewModel(); mymodel.CustEnquiryProduct = custEnquiryProducts.ToList(); mymodel.CustomerEnquiry = customerEnquiries.ToList(); // 返回完整的ViewModel,而不是集合 return View(mymodel); }
步骤2:修正视图中的模型遍历和DisplayNameFor调用
视图中需要遍历Model.CustEnquiryProduct集合,并且DisplayNameFor要针对集合中的单个元素类型来获取名称:
<!-- 修改thead部分的DisplayNameFor --> <thead> <tr> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().TargetPrice) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().Quantity) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().LeadTime) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().Category) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().RoHSStatus) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().SalesRep) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().PackageCase) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().Description) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().Currency.Currency1) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().CustomerEnquiry.SalesRep) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().Product.PartNumber) </th> <th> @Html.DisplayNameFor(model => model.CustEnquiryProduct.FirstOrDefault().RFQManufactuer.Name) </th> <th></th> </tr> </thead> <tbody> <!-- 修改遍历对象为Model.CustEnquiryProduct --> @foreach (var item in Model.CustEnquiryProduct) { <tr> <td> @Html.DisplayFor(modelItem => item.TargetPrice) </td> <td> @Html.DisplayFor(modelItem => item.Quantity) </td> <td> @Html.DisplayFor(modelItem => item.LeadTime) </td> <td> @Html.DisplayFor(modelItem => item.Category) </td> <td> @Html.DisplayFor(modelItem => item.RoHSStatus) </td> <td> @Html.DisplayFor(modelItem => item.SalesRep) </td> <td> @Html.DisplayFor(modelItem => item.PackageCase) </td> <td> @Html.DisplayFor(modelItem => item.Description) </td> <td> @Html.DisplayFor(modelItem => item.Currency.Currency1) </td> <td> @Html.DisplayFor(modelItem => item.CustomerEnquiry.SalesRep) </td> <td> @Html.DisplayFor(modelItem => item.Product.PartNumber) </td> <td> @Html.DisplayFor(modelItem => item.RFQManufactuer.Name) </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.EnqProdID }) | @Html.ActionLink("Details", "Details", new { id = item.EnqProdID }) | @Html.ActionLink("Delete", "Delete", new { id = item.EnqProdID }) </td> </tr> } </tbody>
补充说明
FirstOrDefault()只是用来让编译器识别出集合元素的类型,实际运行时如果集合为空也不会报错,因为DisplayNameFor只解析表达式的元数据,不会执行方法。- 如果不想用
FirstOrDefault(),也可以直接指定类型来获取显示名称:@Html.DisplayNameFor(model => new CustEnquiryProduct().TargetPrice) - 确保
CustEnquiryProduct类的属性上已经通过[Display(Name = "XXX")]特性设置了显示名称,否则DisplayNameFor会直接返回属性名。
内容的提问来源于stack exchange,提问作者Thomas_AndGateSystems
相关产品推荐
相关产品推荐

