复杂对象和模型绑定器 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 控制器操作后是nullFoo 已创建但带有nullBar 字段)。

我认为 MVC 中的模型绑定器将能够创建 FooBar 对象并设置 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 中往返视图数据

您可以在asp.net mvc中将复杂对象从表单发布到控制器吗

Asp.net Core 模型绑定器接受布尔类型的随机整数

为啥 ASP.Net MVC 模型绑定器将空 JSON 数组绑定到 null?

asp.net core mvc 2中抽象类的模型绑定器

自定义模型绑定器未针对 ASP.NET Core 2 中的单个属性触发