如何创建 ViewModel 的 VIew,其中 ViewModel 是带有两个字符串的 IEnumerable?

How to create VIew of ViewModel, where ViewModel is an IEnumerable with two strings?

我一直在研究一个 MVC ViewModel,它包含我需要传递给 View(而不是使用 ViewBags)的标量数据值,以及一个包含进入 table 的表格数据的 IEnumerable。这是基于某人对较早的 SO 问题提出的建议。这样,如果我将标量值作为常量添加到可能有很多行的 table 中,就不会出现重复。

我正在粘贴我的(简化的)数据模型和视图模型定义以及相关的控制器操作。数据结构是我需要的,但是 Visual Studio 不会从此操作创建视图(右键单击并 select "Add View with Model")。

我的问题是,给出了 Action 和 Model 定义,我需要做什么才能创建 View Model 的 View?

我认为自动脚手架是不可能的,因为 ViewModel 没有密钥,所以我期待手动编码。我不知道如何做到这一点,因为我看到的所有示例都显示了 ViewModels 本质上等同于将两个 table 合并为一个更大的 table.

我试过使用@model = [ViewModelName] 和@model = IEnumerable,但都失败了。

二进制响应是,"Duh, the error is telling you exactly what's going on. You don't have a Key." 但该响应无法帮助我解决此问题。如何继续使用 ViewModel 的 this 定义并创建视图?

查看模型

public class StudentRosterViewModel
{        
    // This list is the "table" shown on the view
    public IEnumerable<StudentRoster> StudentRosters { get; set; }

    // These are scalar values apply to the view as a whole
    public string SelectedCampus { get; set; }
    public string SelectedFiscalYear { get; set; }
}

数据模型

[Table("StudentRoster")]
public partial class StudentRoster
{
    public int ID { get; set; }

    [Required]
    [StringLength(3)]
    public string Campus { get; set; }

    [Required]
    [StringLength(4)]
    public string FiscalYear { get; set; }

    [Required]
    [StringLength(50)]        
    public string StudentName { get; set; }

    public int StudentID { get; set; }
}

控制器操作:

public ActionResult FilterableIndex(string campus="MRA",string fiscalYear = "FY16")
{       
    StudentRosterViewModel vm = new StudentRosterViewModel();

    // this is tabular data and goes in the "table"
    vm.StudentRosters = db.StudentRosters
        .Where(m => m.Campus == campus)
        .Where(m => m.FiscalYear == fiscalYear)
        .ToList();

    // These are scalar values; they are not tabular
    // These are needed to supply values to jQuery 
    vm.SelectedCampus = campus;
    vm.SelectedFiscalYear = fiscalYear;

    return View(vm);
}

约定是在您的 Views 文件夹中有一个与您的控制器名称相对应的文件夹。例如,如果控制器是 StudentRosterController,则文件夹将被命名为 StudentRoster.

然后视图将对应于操作的名称 - FilterableIndex.cshtml

.cshtml 文件开头为

@model MvcApp.Controllers.StudentRosterViewModel

(其中 MvcApp.Controllers 是包含您的模型的命名空间。)

感谢评论中的每个人(@StephenMuecke、@LinhTuan)和@ScottHannen 的帮助,我能够创建一个有效且改进的 ViewModel 视图页面。

以下是我所做的结构改进。

  1. 我创建了一个 ViewModels 文件夹并将 ViewModel class 放在那里(不再在 Models 文件夹中)。
  2. 我从 ViewModel 中取出了 [Key] 属性和 Id 属性 -- 这确实不是让它之前看起来有效的原因。
  3. 我右击动作名称并选择"Create View"(没有模型的空)
  4. 视图创建成功。您在下面看到的视图代码是对访问 ViewModel 属性和显示数据的能力的基本测试——我可以添加 Bootstrap 样式和功能,现在我有了一个基本测试。
  5. IEnumerable 需要一些工作才能解包。后来我学会了如何添加一个变量 var headerMeta = Model.StudentRosters.FirstOrDefault(); 以便我可以在 table 正文部分使用 @Html.DisplayNameFor,我又学了一个技巧 (modelItem => item.[column name]).
  6. 可能的进一步步骤是创建自定义 DisplayFor 和 DisplayNameFor 助手,这样我就可以更直接地使用 Html 助手,而无需添加 headerMeta 变量。

我称之为初步成功,因为我终于有数据到达视图。

@model ViewModelTest.Controllers.ViewModels.StudentRosterViewModel

@{
    ViewBag.Title = "FilterableIndex";
    @*This enables us to read the StudentRoster column names*@
    var headerMeta = Model.StudentRosters.FirstOrDefault();
}

<h2>FilterableIndex</h2>

<p>Test for reception of data</p>
<ul>
    <li>Selected campus: @Model.SelectedCampus</li>
    <li>Selected fiscal year: @Model.SelectedFiscalYear</li>
</ul>

@*I'll add bootstrap styles later*@
<table>
    <tr>
        <th>@Html.DisplayNameFor(m => headerMeta.Campus)</th>
        <th>@Html.DisplayNameFor(m => headerMeta.FiscalYear)</th>
        <th>@Html.DisplayNameFor(m => headerMeta.StudentName)</th>
        <th>@Html.DisplayNameFor(m => headerMeta.StudentID)</th>
    </tr>     
@foreach (var item in Model.StudentRosters)
{    
    <tr>        
        <td>@Html.DisplayFor(modelItem => item.Campus)</td>
        <td>@Html.DisplayFor(modelItem => item.FiscalYear)</td>
        <td>@Html.DisplayFor(modelItem => item.StudentName)</td>
        <td>@Html.DisplayFor(modelItem => item.StudentID)</td>
    </tr>
}