为什么在部分视图输入上显式设置Name属性会导致POST后更改值?

时间:2015-12-07 21:45:53

标签: c# asp.net-mvc razor

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

我设置了一个简单的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; }
}

控制器

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);
    }
}

查看/父/显示

@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>
}

查看/儿童/显示

@model TextBoxForBugTest.Models.Child

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

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

Before POST

但是,在提交表单后从更新操作方法返回后,Child.Code已被赋值为&#34; 1&#34; - 父母代码。

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" })

正在生成具有不同&#34;值&#34;的html。第二次被调用(在POST之后)。

我能够通过设置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之后)?

获得POST的表单数据是

enter image description here

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

具有Parent.Code == 1和Child.Code == 999的预期值。

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

  

另请注意新的{Name =&#34; Child.Code&#34; } hack不会更改id属性,并且您的html无效。 - Stephen Muecke

确实,使用

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

,我最终得到2个带有id =&#34; Code&#34;的输入,根据the spec无效。

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

1 个答案:

答案 0 :(得分:3)

您使用@Html.Partial("~/Views/Child/Show.cshtml", Model.Child)正在使用

生成输入
<input name="Code" ... />

然而它需要

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

不要使用partial来生成表单控件。而是使用EditorTemplate生成带有前缀的正确name属性。

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

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

编辑(根据修订后的问题)

解释发生了什么。将视图传递给模型并使用HtmlHelpers为模型的属性生成表单控件时,帮助程序首先计算表达式并获取属性的ModelMetadataModelMetadata包括模型本身的值以及用于确定如何生成html以及如何显示模型值的添加属性。帮助器还添加了TextBoxFor()方法的第二个参数中定义的htmlAttributes。

现在假设您已使用值999编辑了第二个文本框的值,当您提交表单时,其帖子会返回Code=1&Child.Code=999,因为您已为input元素指定了属性{{1 }}。 name="Child.Code"读取表单数据,在模型中找到DefaultModelBinderCode的匹配项,并将其值分别设置为Child.Code1

现在,当您返回视图时,您的第二个999方法绑定到属性TextBoxFor()(不是Code),其值为Child.Code(不是1 })。只添加一个999属性不会改变你绑定的属性(但是当你将数据发送回控制器时它会搞砸模型绑定)。