c# - 为什么在部分 View 输入上显式设置名称属性会导致值在 POST 后发生变化?

标签 c# asp.net-mvc razor

我今天遇到一个问题,其中一个表单中的嵌套对象的 Code 值被更改为不正确的值。经过一番挖掘,我发现只有在 POST 后,并且只有当我尝试使用 Html.TextBoxFor 的第二个对象参数显式设置 Name 属性时,才会为它分配 Parent 对象 Code 的值。

我设置了一个简单的 MVC(版本 5.2.2.0)项目来隔离问题。这是相应的代码。

型号

public class Parent
{
    public string Code { get; set; }
    public Child Child { get; set; } 
}

public class Child
{
    public string Code { get; set; }
}

Controller

public class ParentController : Controller
{
    public ActionResult Show()
    {
        var child = new Child() { Code = "999"};
        var parent = new Parent() { Code = "1", Child = child };

        return View("Show", parent);
    }

    public ActionResult Update(Parent parent)
    {
        return View("Show", parent);
    }
}

View /父级/显示

@model TextBoxForBugTest.Models.Parent

@using (Html.BeginForm("Update", "Parent"))
{
    @Html.TextBoxFor(o => o.Code)
    @Html.Partial("~/Views/Child/Show.cshtml", Model.Child)
    <button type="submit">Submit</button>
}

View /子/显示

@model TextBoxForBugTest.Models.Child

@Html.TextBoxFor(o => o.Code, new { Name = "Child.Code" })

当我第一次加载/Parent/Show 时,我在输入中看到正确的值:1(代码)和 999(子代码)。

Before POST

但是,提交表单后从更新操作方法返回后,Child.Code 已被分配值“1” - 父代码。

After POST

我发现可以通过设置 HtmlFieldPrefix 来解决该问题。

@model TextBoxForBugTest.Models.Child

@{ Html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "Child"; }

@Html.TextBoxFor(o => o.Code)

或者使用局部变量

@model TextBoxForBugTest.Models.Child

@{ var theCode = Model.Code; }

@Html.TextBoxFor(o => theCode, new { Name = "Child.Code" })

但我想了解为什么。这里发生了什么?为什么 POST 后 Child.Code 会被赋予 Parent.Code 的值?

我还发现了一些与使用扩展程序相关的问题,但它们似乎回答了不同的问题

ASP.NET MVC partial views: input name prefixes

ASP.MVC 3 Razor Add Model Prefix in the Html.PartialView extension

***编辑 - 从答案中可以清楚地看出,我在陈述实际问题方面做得很差,因此我将尝试在这里进行更多澄清。

我发现导致最终用户发现错误的问题是

@Html.TextBoxFor(o => o.Code, new { Name = "Child.Code" })

第二次调用时(POST 之后)生成具有不同“值”的 html。

我能够通过设置 Html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix 解决该问题。 Stephen Muecke 还在编辑器模板中指出了该问题的另一个(可能更好)解决方案。

我想问的是:

为什么

@Html.TextBoxFor(o => o.Code, new { Name = "Child.Code" })

生成

<input name="Child.Code" id="Code" type="text" value="999">

第一次(/Parent/Show),但随后生成

<input name="Child.Code" id="Code" type="text" value="1">

第二次(发布到/Parent/Update 之后)?

发布的表单数据是

enter image description here

以及

中的绑定(bind)模型
public ActionResult Update(Parent parent)
{
    return View("Show", parent);
}

预期值为 Parent.Code == 1 和 Child.Code == 999。

我认为 Stephen Muecke 可能接近我在他的评论中寻找的答案

Note also that the new { Name = "Child.Code" } hack does not change the id attribute and you have invalid html. – Stephen Muecke

确实,使用

@Html.TextBoxFor(o => o.Code, new { Name = "Child.Code" })

,我最终得到了 2 个 id="Code"的输入,根据 the spec 这是无效的.

即使知道这一点,我仍然不明白为什么 TextBoxFor 生成的 value 属性根据我是 GET/Parent/Show 还是 POST 到/Parent/Update 而不同。

最佳答案

您使用 @Html.Partial("~/Views/Child/Show.cshtml", Model.Child) 正在生成一个输入

<input name="Code" ... />

虽然需要

<input = name="Child.Code" ... />

不要使用部分来生成表单控件。相反,使用 EditorTemplate 它将生成带有前缀的正确 name 属性。

将您的部分重命名为 Child.cshtml 并将其放置在 /Views/Shared/EditorTemplates 文件夹中,并在主视图中使用

@Html.EditorFor(m => m.Child)

编辑(基于修改后的问题)

解释正在发生的事情。当您将 View 传递给模型并使用 HtmlHelpers 为模型的属性生成表单控件时,帮助器首先计算表达式并获取该属性的 ModelMetadataModelMetadata 包括模型本身的值以及用于确定需要如何生成 html 以及如何显示模型值的附加属性。该帮助器还添加 TextBoxFor() 方法的第二个参数中定义的 htmlAttributes。

现在假设您已将第二个文本框的值编辑为 999,当您提交表单时,它会发回 Code=1&Child.Code=999因为您已经为输入元素指定了一个属性 name="Child.Code"DefaultModelBinder 读取表单数据,在模型中查找 CodeChild.Code 的匹配项,并将它们的值设置为 1分别为 999

现在,当您返回 View 时,您的第二个 TextBoxFor() 方法将绑定(bind)到属性 Code (不是 Child.Code),该属性具有值为 1(不是 999)。仅添加 name 属性不会更改您绑定(bind)到的属性(但当您将数据发送回 Controller 时,它确实会搞砸模型绑定(bind))。

关于c# - 为什么在部分 View 输入上显式设置名称属性会导致值在 POST 后发生变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34143821/

相关文章:

c# - 逐步将 WebForm VB Web 应用程序转换为 MVC C#

c# - 无法序列化 session 状态?

c# - asp.net mvc Bundle.IncludeDirectory 示例?

asp.net-mvc-3 - 将输入值传递给操作 (ASP.Net MVC 3)

asp.net-mvc - 如何更改 .NET CORE 中的默认 ASP.NET Identity 表名称?

c# - 如何在 MVC 3 中通过 jQuery 加载传递对象数组?

asp.net-mvc - ViewData 始终为空

c# - 从异步任务返回 404

c# - .NET Core 查询 Postgres 上的可选过滤器?

c# - 如何在 C# 中在一段时间后自动从一个 Activity 移动到另一个 Activity