复杂对象和模型绑定器 ASP.NET MVC
Posted
技术标签:
【中文标题】复杂对象和模型绑定器 ASP.NET MVC【英文标题】:Complex object and model binder ASP.NET MVC 【发布时间】:2015-03-28 12:27:13 【问题描述】:我有一个模型对象结构,其中包含一个 Foo
类,其中包含一个带有字符串值的 Bar
。
public class Foo
public Bar Bar;
public class Bar
public string Value get; set;
还有一个使用这种结构的视图模型
public class HomeModel
public Foo Foo;
然后我有一个表单,在 Razor 中看起来像这样。
<body>
<div>
@using (html.BeginForm("Save", "Home", FormMethod.Post))
<fieldset>
@Html.TextBoxFor(m => m.Foo.Bar.Value)
<input type="submit" value="Send"/>
</fieldset>
</div>
</body>
在 html 中变成了。
<form action="/Home/Save" method="post">
<fieldset>
<input id="Foo_Bar_Value" name="Foo.Bar.Value" type="text" value="Test">
<input type="submit" value="Send">
</fieldset>
</form>
最后是这样处理 post loos 的控制器
[HttpPost]
public ActionResult Save(Foo foo)
// Magic happends here
return RedirectToAction("Index");
我的问题是Foo
中的Bar
在遇到Save
控制器操作后是null
(Foo
已创建但带有null
Bar
字段)。
我认为 MVC 中的模型绑定器将能够创建 Foo
和 Bar
对象并设置 Value
属性,只要它看起来像上面那样。我错过了什么?
我也知道我的视图模型有点过于复杂并且可能更简单,但对于我正在尝试做的事情,如果我可以使用更深层次的对象结构,我真的会帮助我。上面的示例使用 ASP.NET 5。
【问题讨论】:
你的post方法参数需要是HomeModel
或者你需要使用[Bind(Prefix="foo")]
属性。但是你永远不应该在 ActionResult
方法中命名一个与模型属性同名的参数(绑定会失败)
@StephenMuecke 感谢您的回复。但是,我确实更改为在 Save
操作中接收 HomeModel
并更改了参数名称 - 但在 Foo 中仍然是一个空对象 ...
那是因为它是一个字段,而不是一个属性。添加 getter 和 setter :)
@StephenMuecke 该死……我以为我可以使用字段……没想到要改变它……谢谢!如果您想要积分,只需将您的评论更改为帖子即可。
@Riri,感谢您提供一个简单的示例。我不得不承认,我对相反的情况发生的频率感到不安(当然,这纯粹是提问者的错)。如果这是一个元注释,我很抱歉,但是在经历了这么多关于这个相关问题的相反臃肿的例子之后,它令人耳目一新。
【参考方案1】:
首先,DefaultModelBinder
不会绑定到字段,所以你需要使用属性
public class HomeModel
public Foo Foo get; set;
其次,助手正在生成基于HomeModel
的控件,但您发回Foo
。要么将 POST 方法更改为
[HttpPost]
public ActionResult Save(HomeModel model)
或使用BindAttribute
来指定Prefix
(这实际上是从发布的值中去除前缀的值 - 所以Foo.Bar.Value
变为Bar.Value
用于绑定)
[HttpPost]
public ActionResult Save([Bind(Prefix="Foo")]Foo model)
还请注意,您不应将方法参数命名为与您的属性之一相同的名称,否则绑定将失败并且您的模型将为空。
【讨论】:
“不应将方法参数命名为与您的属性之一相同的名称,否则绑定将失败并且您的模型将为空。” ——感谢一百万的这张纸条!但是哦,天哪,多么令人沮丧的问题。我敢肯定存在一些技术上的困难,但他们肯定可以克服这个意想不到且难以发现的限制。即使是这样,在找到这篇文章之前我已经阅读了很多文章,而且你很容易就不会放这个。在我看来,克服这个限制是值得的。无论如何,谢谢一百万。 @NicholasPetersen,你说得对,它令人沮丧且不明显,但由于模型绑定的工作方式,确实没有办法克服这一点。在this answer 中更详细地解释了实际行为。可惜没有更好的记录。 在过去的几个小时里,我一直在为属性与字段的问题拉扯头发。我不敢相信这是这么简单的事情。呜呜呜【参考方案2】:我刚刚发现了发生这种情况的另一个原因,即如果您的财产被命名为Settings
!考虑以下视图模型:
public class SomeVM
public SomeSettings DSettings get; set; // named this way it will work
public SomeSettings Settings get; set; // property named 'Settings' won't bind!
public bool ResetToDefault get; set;
在代码中,如果您绑定到Settings
属性,则绑定失败(不仅在发布时,甚至在生成表单时)。如果您将 Settings
重命名为 DSettings
(等),它会突然再次起作用。
【讨论】:
【参考方案3】:我遇到了同样的问题,在我按照@Stephen Muecke 的步骤操作后,我意识到问题是因为我的输入被禁用(我在文档就绪时使用 JQuery 禁用它们),您可以在此处看到:How do I submit disabled input in ASP.NET MVC?。最后我使用只读而不是禁用属性,所有值都成功发送到控制器。
【讨论】:
【参考方案4】:我遇到了同样的问题,但是一旦我为外键创建了一个 HIDDEN FIELD ......一切都很好......
表格示例:
@using (Html.BeginForm("save", "meter", FormMethod.Post))
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
@Html.HiddenFor(model => Model.Entity.Id)
@Html.HiddenFor(model => Model.Entity.DifferentialMeter.MeterId)
@Html.HiddenFor(model => Model.Entity.LinearMeter.MeterId)
@Html.HiddenFor(model => Model.Entity.GatheringMeter.MeterId)
... all your awesome controls go here ...
行动示例:
// POST: /Meter/Save
[HttpPost]
public ActionResult Save(Meter entity)
... world-saving & amazing logic goes here ...
漂亮的图片:
【讨论】:
以上是关于复杂对象和模型绑定器 ASP.NET MVC的主要内容,如果未能解决你的问题,请参考以下文章
您可以在asp.net mvc中将复杂对象从表单发布到控制器吗