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

Model Binding on Save isn't returning full List of Nested Objects, maximum of one

保存时的模型绑定不是 return 嵌套对象的完整列表,最多一个

试图理解 EditorTemplates,这是一个目前无法正常工作的简单示例。模型没有 return 完整的 IList 的项目,似乎每次 returning 1 个对象,而在我的示例中应该有 2 个对象。

当我编辑ModelA时,例子是:

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

此列表在编辑期间正确显示,但在保存时,一些信息是 "lost",在下面添加 (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)

基本上,在保存时,模型不会 return 从列表中提取一个以上的对象。

类:

基础对象:

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

}

模型 A、模型 B 和模型 C:

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(对于模型 A):

@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 对象的绑定看起来会被正确绑定(但它只是 return第一个对象):

型号 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">

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

型号 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">

POST 表单数据:

@Html.EditorFor(...) POST (没有更新 ModelCs 的 ModelBs 列表):

__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 中。有些东西被切断了>(我仔细检查并三次检查我没有遗漏任何东西)

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

...谜团继续

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

我在查看POST资料时发现,它显示了第二个ModelB的a(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>