MVC表单对于复杂的模型对象,后反序列化是不完整的

时间:2013-07-24 14:25:23

标签: asp.net-mvc-4 serialization razor http-post

使用MVC 4 Forms,我的模型总是包含List<T>属性中的四个子项。视图使用Razor局部视图渲染的四个子模型中的每一个都正确显示模型。问题是,当我提交/发布时,模型反序列化为子列表的空值。

型号:

public class MyModel
{
    public int SomeValue { get; set; }

    public List<ChildModel> Children { get; set; }

    ...
}

查看:

@model MyProject.Models.MyModel

@using (Html.BeginForm())
{
    @Html.LabelFor(model => model.SomeValue)

    @Html.Partial("ChildPartial", Model.Children[0])
    @Html.Partial("ChildPartial", Model.Children[1])
    @Html.Partial("ChildPartial", Model.Children[2])
    @Html.Partial("ChildPartial", Model.Children[3])

    <input type="submit" value="Save" />
}

控制器:

public class MyController : Controller
{
    public ActionResult Index()
    {
        MyModel model = new MyModel();
        model.Children = new List<ChildModel>();

        model.Children.Add(new ChildModel());
        model.Children.Add(new ChildModel());
        model.Children.Add(new ChildModel());
        model.Children.Add(new ChildModel());

        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyModel model)
    {
        //model.Children is null here

        //do stuff
        ...

        return RedirectToAction("Index", "SomeOtherController");
    }
}

每个ChildPartial视图都正确呈现,我在控件中输入值,但它们不会反序列化为List<ChildModel>。我只能在Post方法中获取MyModel的根级别属性来反序列化。

我尝试将UpdateModel(model);添加到控制器Post方法的开头,但没有运气。有什么想法吗?

修改

ChildModel.cs:

public class ChildModel
{
    public String Name { get; set; }
    public double Ratio { get; set; }
    ...
}

ChildPartial.cshtml:

@model MyProject.Models.ChildModel

<div>
    <div>
        <div>
            <span>@Model.Name</span>
        </div>
        <div>
            @Html.LabelFor(m => m.Ratio)
            @Html.TextBoxFor(m => m.Ratio, new { autocomplete = "off" })
            @Html.ValidationMessageFor(m => m.Ratio)
        </div>
    </div>

    ...
</div>

1 个答案:

答案 0 :(得分:3)

我首先建议您阅读默认模型绑定器所需的特定语法以及绑定到集合时的命名约定:http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

将输入字段的名称与本博文中说明的名称进行比较后,您将很快理解为什么您的代码不起作用。您根本不遵循标准命名约定。

为了解决这个问题,我建议您使用编辑器模板。因此,在您的主视图中输入以下内容:

@model MyProject.Models.MyModel

@using (Html.BeginForm())
{
    @Html.LabelFor(model => model.SomeValue)

    @Html.EditorFor(model => model.Children)

    <input type="submit" value="Save" />
}

然后将ChildPartial.cshtml移至~/Views/Shared/EditorTemplates/ChildModel.cshtml。请注意,模板的名称和位置非常重要。确保你已经遵循它。把它放在里面:

@model MyProject.Models.ChildModel

<div>
    <div>
        <div>
            <span>@Model.Name</span>
        </div>
        <div>
            @Html.LabelFor(m => m.Ratio)
            @Html.TextBoxFor(m => m.Ratio, new { autocomplete = "off" })
            @Html.ValidationMessageFor(m => m.Ratio)
        </div>
    </div>

    ...
</div>

好了,现在运行你的项目,检查生成的HTML,更具体地说,输入字段的名称将它们与你的初始版本进行比较,并将它们与我最初在我的答案中链接到的博客文章进行比较,你将了解所有关于模型绑定到集合如何在ASP.NET MVC中工作。

备注:在您的子模板中,您没有ChildModel的Name属性的相应输入字段。因此,如果控制器中为空,请不要感到惊讶。提交表单时,您根本不会向其发送值。如果您希望这种情况发生,您可以将其作为隐藏字段包含在编辑器模板中:

@Html.HiddenFor(m => m.Name)