我今天遇到了一个问题,其中我们其中一个表单中的嵌套对象的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)。
但是,在提交表单后从更新操作方法返回后,Child.Code已被赋值为&#34; 1&#34; - 父母代码。
我发现我可以通过设置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的表单数据是
和
中的绑定模型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而不同。
答案 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为模型的属性生成表单控件时,帮助程序首先计算表达式并获取属性的ModelMetadata
。 ModelMetadata
包括模型本身的值以及用于确定如何生成html以及如何显示模型值的添加属性。帮助器还添加了TextBoxFor()
方法的第二个参数中定义的htmlAttributes。
现在假设您已使用值999
编辑了第二个文本框的值,当您提交表单时,其帖子会返回Code=1&Child.Code=999
,因为您已为input元素指定了属性{{1 }}。 name="Child.Code"
读取表单数据,在模型中找到DefaultModelBinder
和Code
的匹配项,并将其值分别设置为Child.Code
和1
现在,当您返回视图时,您的第二个999
方法绑定到属性TextBoxFor()
(不是Code
),其值为Child.Code
(不是1
})。只添加一个999
属性不会改变你绑定的属性(但是当你将数据发送回控制器时它会搞砸模型绑定)。