C# MVC 使用一个模型发送和检索列表<> to/from 视图

C# MVC send and retrieve a list<> to/from a view using one model

我正在尝试编写一个页面,从 SQL table 中检索 List<> 并显示它,以便用户可以进行更改。我已经能够显示信息了。我 运行 遇到了提交问题。我无法找到一种方法来获取输入的数据并将更改发送到 List<>,以便我可以在 controller/model 中对其进行操作。

目前我在提交时将新数据放入数组中。因为我在视图中使用 @model List<modelname>。是否可以替换值或可能将数据放入视图中的新列表?

你绝对可以做到这一点,但人们最初确实倾向于 运行 遇到问题,所以我将提供一个完整的例子。

首先,让我们定义一个 HomeController,它有一个 Index 动作,returns 一个 List<EmployeeViewModel>:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        // Assume this would really come from your database.
        var employees = new List<EmployeeViewModel>()
        {
            new EmployeeViewModel { Id = 1, Name = "Employee 1" },
            new EmployeeViewModel { Id = 2, Name = "Employee 2" },
            new EmployeeViewModel { Id = 3, Name = "Employee 3" },
        };

        return View(employees);
    }

    [HttpPost]
    public ActionResult Index(List<EmployeeViewModel> employees)
    {
        // Rest of action
    }
}

人们首先解决这个问题的典型方法是在 index.cshtml 中做类似下面的事情:

@model List<EmployeeViewModel>

@using (Html.BeginForm())
{
    foreach (var item in Model)
    {
        <div class="row">
            @Html.EditorFor(x => item.Id)
        </div>
        <div class="row">
            @Html.EditorFor(x => item.Name)
        </div>
    }

    <input type="submit" />
}

乍一看,这似乎行得通。但是,如果您在 POST 操作中放置一个断点并单击提交按钮,您会注意到 employeesnull。这是因为 foreach 循环正在生成 HTML,如下所示:

<input name="item.Id" type="number" value="1" />
<input name="item.Name" type="text" value="Employee 1" />
<input name="item.Id" type="number" value="2" />
<input name="item.Name" type="text" value="Employee 2" />
<input name="item.Id" type="number" value="3" />
<input name="item.Name" type="text" value="Employee 3" />

我已经删除了一些不相关的部分,但请注意每个员工的 name 属性如何具有相同的值。当默认模型绑定器尝试在服务器上构建数据列表时,它必须能够区分不同的员工,并且因为它不能,它导致列表为 null.

那么为什么它会生成相同的值?是因为:

@Html.EditorFor(x => item.Id)
@Html.EditorFor(x => item.Name)

我们没有将索引值传递给 HtmlHelper 方法调用,因此信息不会进入生成的 HTML。我们可以简单地通过使用 for 循环来解决这个问题:

for (int i = 0; i < Model.Count; i++)
{
    @Html.EditorFor(x => Model[i].Id)
    @Html.EditorFor(x => Model[i].Name)
}

由于我们现在为每个方法调用提供一个索引,因此生成的 HTML 现在确实包含每个员工的索引:

<input name="[0].Id" type="number" value="1" />
<input name="[0].Name" type="text" value="Employee 1" />
<input name="[1].Id" type="number" value="2" />
<input name="[1].Name" type="text" value="Employee 2" />
<input name="[2].Id" type="number" value="3" />
<input name="[2].Name" type="text" value="Employee 3" />

这允许默认模型绑定器将 IdName 与每个 EmployeeViewModel 相关联,从而使其能够在服务器上正确构造类型。

至此,您的问题已解决,但是,如果可以避免,不建议使用 for 循环,这会将我们带到编辑器模板。编辑器模板是 HtmlHelper 方法,允许我们为给定类型呈现自定义模板(即视图)。因此,让我向您展示如何使用上面的示例代码执行此操作的示例。

首先,您需要执行以下操作:

  1. ~/Views/Home/ 文件夹中创建一个 EditorTemplates 文件夹(EditorTemplates 名称在 MVC 中具有特殊含义,因此正确拼写很重要)。
  2. 在该文件夹内创建一个 EmployeeViewModel.cshtml 视图(同样,名称在这里很重要:它应该与您希望为其创建自定义模板的类型的名称相匹配)。

完成后,它应该如下所示:

现在,打开 EmployeeViewModel.cshtml,将 Index.cs 中的渲染代码放入其中:

@model EmployeeViewModel

<div class="row">
    @Html.EditorFor(x => x.Id)
</div>
<div class="row">
    @Html.EditorFor(x => x.Name)
</div>

最后,打开index.cshtml,改成如下:

@model List<EmployeeViewModel>

@using (Html.BeginForm())
{
    @Html.EditorFor(x => x)

    <input type="submit" />
}

Html.EditorForHtml.DisplayFor 都足够聪明,可以识别何时为集合调用它们,因此它们寻找自定义模板来呈现集合的类型(在本例中, EditorFor 将为 EmployeeViewModel).

寻找编辑器模板

由于我们提供了一个编辑器模板,它不仅会为集合中的每个项目呈现它,还会为每个项目生成正确的索引,为模型绑定器提供它需要的所有信息在服务器上重建该集合。

最终结果是模型绑定变得更简单,不仅视图的代码更简单,而且还根据所涉及的类型进行了拆分,这使您的视图更易于使用,而不是无所不能的大视野。

还要提一下,因为我的例子是直接使用集合,仅此而已,你实际上可以用@Html.EditorForModel()替换@Html.EditorFor(x => x)。最初我没有这样做,因为我不想给人留下这样的印象,即仅使用后者即可调用模板。