Save上的模型绑定不会返回完整的嵌套对象列表,最多只能返回一个

时间:2017-11-15 14:25:08

标签: c# asp.net-mvc model-binding mvc-editor-templates

保存时的模型绑定不会返回完整的嵌套对象列表,最多只能返回一个

尝试理解EditorTemplates,这是一个目前无法运行的简单示例。模型没有返回完整的IList 对于项目,每次在我的例子中应该有2个时,它似乎返回1个对象。

当我编辑ModelA时,示例是:

- Name: Model A
    List of Model Bs:
        - Name: Model B1
            List of Model Cs:
                - Name: Model C1
                - Name: Model C2
        - Name: Model B2

此列表在编辑期间正确显示,但在保存时,部分信息“丢失”,在下方添加(not found)

- Name: Model A (in Model)
    List of Model Bs:
        - Name: Model B1 (in Model)
            List of Model Cs:
                - Name: Model C1 (in Model)
                - Name: Model C2 (not found)
        - Name: Model B2 (not found)

基本上,在保存时,模型不会从列表中返回任何多个对象。

类:

BaseObject:

public class BaseObject
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public Guid Oid { get; set; }

}

ModelA,ModelB和ModelC:

public class ModelA : BaseObject
{
    public string Name { get; set; }
    public virtual IList<ModelB> ModelBs { get; set; }
}

public class ModelB : BaseObject
{
    public string Name { get; set; }

    public Guid? ModelAID { get; set; }

    [ForeignKey("ModelAID")]
    public virtual ModelA ModelA { get; set; }

    public virtual IList<ModelC> ModelCs { get; set; }
}

public class ModelC : BaseObject
{
    public string Name { get; set; }

    public Guid? ModelBID { get; set; }

    [ForeignKey("ModelBID")]
    public virtual ModelB ModelB { get; set; }
}

ModelA的基本控制器,这里是编辑:

public ActionResult Edit(Guid? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    ModelA modelA = db.ModelAs.Find(id);
    if (modelA == null)
    {
        return HttpNotFound();
    }
    return View(modelA);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ModelA modelA)
//Removed: [Bind(Include = "Oid,Name,DateCreated,DateUpdated,DateDeleted,IsDeleted")] 
{
    if (ModelState.IsValid) //Break point to review "modelA"
    {
        db.Entry(modelA).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(modelA);
}

查看:

Edit.cshtml(适用于ModelA):

@model x.Models.Nesting.ModelA

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.EditorForModel(Model)
}

Views / Shared / EditorTemplates中的视图:

ModelA.cshtml:

@model x.Models.Nesting.ModelA

<div class="form-horizontal">
    <h4>ModelA</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(model => model.Oid)

    <div class="form-group">
        @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.ModelBs, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">

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

            @* Tried the code below as well *@
            @*
            @for (var i = 0; i < Model.ModelBs.Count(); i++)
            {
                @Html.EditorFor(m => m.ModelBs[i])
            }
            *@

        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Save" class="btn btn-default" />
        </div>
    </div>
</div>

ModelB.cshtml:

@model x.Models.Nesting.ModelB
@using (Html.BeginForm())
{
    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Oid)
        @Html.HiddenFor(model => model.ModelAID)

        <div class="form-group">
            @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.ModelCs, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">

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

                @* Tried the code below as well *@
                @*
                @foreach(var item in Model.ModelCs)
                {
                    @Html.EditorFor(x => item)
                }
                *@

            </div>
        </div>

    </div>
}

ModelC.cshtml:

@model x.Models.Nesting.ModelC

@using (Html.BeginForm())
{
    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Oid)
        @Html.HiddenFor(model => model.ModelBID)

        <div class="form-group">
            @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
        </div>

    </div>
}

作为HTML中的示例(当使用@Html.EditorFor(model => model.ModelBs)时),两个ModelB对象的绑定看起来看起来都是正确绑定的(但它只返回第一个对象):

模特B1:

<input 
    data-val="true" data-val-required="The Oid field is required." 
    id="ModelBs_f1aa613f-96f4-427f-a568-c70556ad2117__Oid" 
    name="ModelBs[f1aa613f-96f4-427f-a568-c70556ad2117].Oid" 
    type="hidden" 
    value="7e99950b-62c9-e711-afd4-7cb0c2b5b934">

<input 
    id="ModelBs_f1aa613f-96f4-427f-a568-c70556ad2117__ModelAID" 
    name="ModelBs[f1aa613f-96f4-427f-a568-c70556ad2117].ModelAID" 
    type="hidden" 
    value="7e99950b-62c9-e711-afd4-7cb0c2b5b935">

<input 
    class="form-control text-box single-line valid" 
    id="ModelBs_f1aa613f-96f4-427f-a568-c70556ad2117__Name" 
    name="ModelBs[f1aa613f-96f4-427f-a568-c70556ad2117].Name" 
    type="text" 
    value="Model B1" aria-invalid="false">

模型B2:

<input 
    data-val="true" data-val-required="The Oid field is required." 
    id="ModelBs_128318da-af85-46a5-bc0d-71361610d989__Oid" 
    name="ModelBs[128318da-af85-46a5-bc0d-71361610d989].Oid" 
    type="hidden" 
    value="7e99950b-62c9-e711-afd4-7cb0c2b5b936">

<input 
    id="ModelBs_128318da-af85-46a5-bc0d-71361610d989__ModelAID" 
    name="ModelBs[128318da-af85-46a5-bc0d-71361610d989].ModelAID" 
    type="hidden" 
    value="7e99950b-62c9-e711-afd4-7cb0c2b5b935">

<input 
    class="form-control text-box single-line valid" 
    id="ModelBs_128318da-af85-46a5-bc0d-71361610d989__Name" 
    name="ModelBs[128318da-af85-46a5-bc0d-71361610d989].Name" 
    type="text" 
    value="Model B2" aria-invalid="false">

在ModelA中使用for循环时,循环ModelB(@for (var i = 0; i < Model.ModelBs.Count(); i++) { @Html.EditorFor(m => m.ModelBs[i]) }),索引是数字,但仍然不返回完整的模型。

模特B1:

<input 
    data-val="true" data-val-required="The Oid field is required." 
    id="ModelBs_0__Oid" name="ModelBs[0].Oid" 
    type="hidden" 
    value="7e99950b-62c9-e711-afd4-7cb0c2b5b934">

<input 
    id="ModelBs_0__ModelAID" 
    name="ModelBs[0].ModelAID" 
    type="hidden" 
    value="7e99950b-62c9-e711-afd4-7cb0c2b5b935">

<input 
    class="form-control text-box single-line valid" 
    id="ModelBs_0__Name" name="ModelBs[0].Name" 
    type="text" 
    value="Model B1" aria-invalid="false">

模型B2:

<input 
    data-val="true" data-val-required="The Oid field is required." 
    id="ModelBs_1__Oid" name="ModelBs[1].Oid" 
    type="hidden" 
    value="7e99950b-62c9-e711-afd4-7cb0c2b5b936">

<input 
    id="ModelBs_1__ModelAID" name="ModelBs[1].ModelAID" 
    type="hidden" 
    value="7e99950b-62c9-e711-afd4-7cb0c2b5b935">

<input 
    class="form-control text-box single-line valid" 
    id="ModelBs_1__Name" name="ModelBs[1].Name" 
    type="text" 
    value="Model B 2" aria-invalid="false">

投递表格数据

@Html.EditorFor(...) POST (未更新ModelB的ModelC列表)

__RequestVerificationToken:1PfXHdYtb5eE-j6g4DWBEZiRa0trOL8UvYGKVjL0pxR1qOjQE52be7UB14VaIJRpp5UA1Iz9WXt4g_7LKixKhK7ah7Hjp6hOLmLa1m7XavI1
Oid:7e99950b-62c9-e711-afd4-7cb0c2b5b935
Name:Model A1
ModelBs.index:4a481093-9bdd-43ae-b84b-144c576ff346
ModelBs[4a481093-9bdd-43ae-b84b-144c576ff346].Oid:7e99950b-62c9-e711-afd4-7cb0c2b5b934
ModelBs[4a481093-9bdd-43ae-b84b-144c576ff346].ModelAID:7e99950b-62c9-e711-afd4-7cb0c2b5b935
ModelBs[4a481093-9bdd-43ae-b84b-144c576ff346].Name:Model B1
ModelBs[4a481093-9bdd-43ae-b84b-144c576ff346].ModelCs[0].Oid:7e99950b-62c9-e711-afd4-7cb0c2b5b936
ModelBs[4a481093-9bdd-43ae-b84b-144c576ff346].ModelCs[0].ModelBID:7e99950b-62c9-e711-afd4-7cb0c2b5b934
ModelBs[4a481093-9bdd-43ae-b84b-144c576ff346].ModelCs[0].Name:Model C 1
ModelBs.index:a1dd130d-a1f7-47ed-90a2-28055a960c9b

For循环POST:

__RequestVerificationToken:x9hpnm-c1g0Cm9gTnSRjCFIVflziqXqiO3iFkzVpMc33gnNlBoDsvwBHMmRT38sWTCGrFSqCqzcuuBZdXLsXTgX1EbkqUSqPuAtwUrR1XXA1
Oid:7e99950b-62c9-e711-afd4-7cb0c2b5b935
Name:Model A1
ModelBs[0].Oid:7e99950b-62c9-e711-afd4-7cb0c2b5b934
ModelBs[0].ModelAID:7e99950b-62c9-e711-afd4-7cb0c2b5b935
ModelBs[0].Name:Model B1
ModelBs[0].ModelCs[0].Oid:7e99950b-62c9-e711-afd4-7cb0c2b5b936
ModelBs[0].ModelCs[0].ModelBID:7e99950b-62c9-e711-afd4-7cb0c2b5b934
ModelBs[0].ModelCs[0].Name:Model C 1

EditorFor有两次ModelBs.index(希望这会导致一个解决方案),但不会在For中。切断的东西&gt; (我加倍检查,我没有错过任何进一步的东西)

FireFox POST数据,看起来像0和1的索引在某种程度上起作用:

__RequestVerificationToken  qlrlO7Z0_byGZYLGJ6Tbx5Fzmpd0dd6b-JPac4V-f1U-17v06OQr27dYZPh_VmRI3X4nGj7ZAOHtBdERnuZscJlNlgoHAqdeXaNQN04e2qE1
Oid 7e99950b-62c9-e711-afd4-7cb0c2b5b935
Name    Model+A1
ModelBs.index   […]
0   3d003e30-d350-4fb0-becd-f65207b033c4
1   be4e35c3-3fee-4c7f-9ee7-580b6e0c8169
ModelBs[3d003e30-d350-4fb0-becd-f65207b033c4].Oid   7e99950b-62c9-e711-afd4-7cb0c2b5b934
ModelBs[3d003e30-d350-4fb0-becd-f65207b033c4].ModelAID  7e99950b-62c9-e711-afd4-7cb0c2b5b935
ModelBs[3d003e30-d350-4fb0-becd-f65207b033c4].Name  Model+B1
ModelBs[3d003e30-d350-4fb0-becd-f65207b033c4].ModelCs[0].Oid    7e99950b-62c9-e711-afd4-7cb0c2b5b936
ModelBs[3d003e30-d350-4fb0-becd-f65207b033c4].ModelCs[0].ModelBID   7e99950b-62c9-e711-afd4-7cb0c2b5b934
ModelBs[3d003e30-d350-4fb0-becd-f65207b033c4].ModelCs[0].Name   Model+C+1

......神秘感仍在继续

1 个答案:

答案 0 :(得分:0)

ModelB和ModelC的EditorTemplate文件(cshtml)有@using (Html.BeginForm()) { ... },删除它解决了问题,允许POST包含所有信息。将BeginForm保留在ModelA上,这是必需的。

我在查看POST信息时发现了它,它显示了第二个ModelB的(n).index,但是没有关联的数据,它下面还有一个form标记,每个人都知道很多表单标签并不好。

更新了cshtml

ModelB.cshtml:

@model x.Models.Nesting.ModelB

<div class="form-horizontal">

    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(model => model.Oid)
    @Html.HiddenFor(model => model.ModelAID)

    <div class="form-group">
        @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.ModelCs, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">

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

            @* Tried the code below as well *@
            @*
            @foreach(var item in Model.ModelCs)
            {
                @Html.EditorFor(x => item)
            }
            *@

        </div>
    </div>

</div>

ModelC.cshtml:

@model x.Models.Nesting.ModelC

<div class="form-horizontal">

    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(model => model.Oid)
    @Html.HiddenFor(model => model.ModelBID)

    <div class="form-group">
        @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
        </div>
    </div>

</div>