Html.Hidden()没有绑定foreach循环中的正确值

时间:2015-03-25 02:09:28

标签: asp.net asp.net-mvc razor

我有一个View,它使用foreach循环显示带有删除选项的学生列表。但是Html.hidden没有按预期获得正确的student.ID。

这是我的学生班:

public class Student
{
    public Guid ID { get; set; }
    public string Name { get; set; }
}

我的控制器

public ActionResult Index()
{
    var students = db.Students.OrderBy(s => s.ID).ToList();
    return View(students);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteStudent(Guid Id)
{
    if (Id != null)
    {
        Student student = db.Students.Single(s => s.ID == Id);
        if (student == null)
        {
            return View("Error");
        }
        db.Students.Remove(student);
        db.SaveChanges();

        return RedirectToAction("Index");
    }
    return View("Error");
}

我的观点

@model IEnumerable<MyProject.Models.Student>
@{ ViewBag.Title = "Student List"; }
<h2>@ViewBag.Title</h2>

<ul>
@foreach (var student in Model)
{
    <li>
    @Html.displayFor(modelItem => student.Name)
    @using (Html.BeginForm("DeleteStudent", "MyController", FormMethod.Post))
    {
        @Html.AntiForgeryToken()
        @Html.Hidden("Id", student.ID)
        <input type="submit" value="Delete" class="btnSubmit">
    }
    </li>
}
</ul>

假设有3名学生的ID分别为1,2和3。但是当我尝试删除学生3时,在控制器上,参数Guid Id的值为1,因此ID = 1的学生将被删除。

事实上,无论我删除哪个学生,Id总是取名单上第一个学生的价值。

我做错了什么,或者更好的方法是什么?

2 个答案:

答案 0 :(得分:3)

修订答案

再次了解您的要求,@Html助手并没有给您带来任何好处。您的基本设置是生成大量表单,每个表单只是为了删除一个项目而设计的。最低要求是识别物品的某种方式。目前的问题是,@Html.Hidden同时生成name="ID"id="ID",并且不允许重复的ID,无法正确处理。

我可以通过以下几种方法来解决这个问题:

  1. 将id作为参数传递给表单本身(我的首选解决方案)
  2. e.g。

    @using (Html.BeginForm("DeleteStudent", "MyController", FormMethod.Post, new {id=student.ID})){
    

    控制器只接受id值作为参数。这意味着您不需要隐藏字段。

    1. 通过手动生成的隐藏字段
    2. 提供单个隐藏值

      e.g。

      <input name="ID" value="@student.ID" type="hidden"/>
      

      控制器只接受id值作为参数。

      1. 如下所述使用@Html.HiddenFor,但控制器操作采用通用FormCollection参数,您可以拉出单独命名的元素(现在可能是最差的选项)。
      2. 原始答案

        通常,尽可能尝试使用@Html.xxxFor方法。然后,他们为回发正确命名项目。

        作为第二条规则,您不能使用foreach外观来呈现@Html.xxxFor,因为这些方法实际上解释了表达式并查找索引值。 foreach不提供索引值,因此您必须使用简单的for循环,以便表达式看起来像Model[i].Property

        e.g。

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

        表达式中存在的索引会在输入字段中生成name="Name[0]"name="Name[1]"等。

        回到手头的问题:对于每个表单,您需要一个隐藏字段,因此您应该使用for循环和@Html.HiddenFor,如下所示:

        for (int i = 0; i < Model.length; i++)
        {
            @using (Html.BeginForm("DeleteStudent", "MyController", FormMethod.Post))
            {
                @Html.AntiForgeryToken()
                @Html.HiddenFor(x=>Model[i].ID)
                <input type="submit" value="Delete" class="btnSubmit">
            }
        }
        

        此外,正如评论中所述,您的观看次数model似乎有误。通常,您会使用IEnumerableIList之类的:

        @model IEnumerable<MyProject.Models.Student>
        

答案 1 :(得分:0)

我怀疑你总是获得相同GUID的原因是因为所有隐藏的输入控件都具有相同的ID。

如果内存服务,当MVC尝试将参数映射到表单元素时,它将匹配与参数名称匹配的第一个字段,查询字符串变量等(不区分大小写)。

您的表单有多个隐藏的输入控件,全部名为“ID”。只有第一个将映射到您的DeleteStudent操作的ID参数。其余的可能会被忽略。