当在 MVC 5 中使用 ajax.actionlink 动态调用同一局部视图的多个实例时,模型绑定不起作用

Model binding doesn't work when multiple instances of the same partial view are called dynamically with ajax.actionlink in MVC 5

我正在构建一个餐厅预订系统。当用户创建预订时,他们还可以决定将在预订中使用哪些家具。为此,我创建了一个局部视图,因此每次用户单击 "add furniture" 时,局部视图都会加载 ajax,然后用户可以在其中指定家具的种类和数量。他们可以根据需要添加任意数量的家具,这意味着他们可以根据需要多次调用分部视图。我的问题来自模型绑定器,然后模型绑定器将所有这些部分视图实例绑定到类型 "BistroReservations_ReservationsFurniture" 的列表对象。请帮助如何使模型活页夹工作。

MainView(请查看底部的 ajax.actionlink 调用 "Add Furniture"

@using (Html.BeginForm()) 

{ @Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>BistroReservations_Reservation</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.DateOfArrival, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.DateOfArrival, new { htmlAttributes = new { @class = "form-control datecontrol" } })
            @Html.ValidationMessageFor(model => model.DateOfArrival, "", new { @class = "text-danger" })
        </div>
    </div>

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

<div class="form-group">
    @Html.LabelFor(model => model.BistroReservations_GuestID, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-6">
        @Html.DropDownList("BistroReservations_GuestID", null, htmlAttributes: new { @class = "form-control" })     
        @Html.ValidationMessageFor(model => model.BistroReservations_GuestID, "", new { @class = "text-danger " })

        @Ajax.ActionLink("Create a New Guest", "NewGuest", new AjaxOptions
       {
           HttpMethod = "GET",
           UpdateTargetId = "NewGuest",
           InsertionMode = InsertionMode.ReplaceWith,                              
       })            
    </div>
</div>       

    <div id="NewGuest" class="form-group">
    </div>
    <br />

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

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

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

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

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

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

            @Ajax.ActionLink("Add Furniture", "Furniture", new AjaxOptions
       {
           HttpMethod = "GET",
           UpdateTargetId = "Furniture",
           InsertionMode = InsertionMode.InsertAfter
       })

        </div>
    </div>



    <div id="Furniture" class="form-group">
    </div>

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

}

返回家具局部视图的ActionResult

public PartialViewResult Furniture()
    {

        ViewBag.BistroReservations_FurnitureID = new SelectList(db.BistroReservations_Furnitures, "BistroReservations_FurnitureID", "Description");

        return PartialView("_Furniture");
    }

家具局部视图

@model CdvPortal.Models.BistroReservations.BistroReservations_ReservationFurniture

<div class="form-horizontal">
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(model => model.BistroReservations_FurnitureID, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownList("BistroReservations_FurnitureID", null, htmlAttributes: new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.BistroReservations_FurnitureID, "", new { @class = "text-danger" })
        </div>
    </div>

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

家具模型

 public class BistroReservations_ReservationFurniture
{
    public int BistroReservations_ReservationFurnitureID { get; set; }

    public int BistroReservations_ReservationID { get; set; }       
    public virtual BistroReservations_Reservation BistroReservations_Reservation { get; set; }

    [Display(Name="Furniture Type")]
    public int BistroReservations_FurnitureID { get; set; }
    public virtual BistroReservations_Furniture BistroReservations_Furniture { get; set; }

    public int Quantity { get; set; }
}

@Ajax.ActionLink() 方法返回的视图具有重复的 id 属性(无效 html)和重复的 name 属性,这些属性没有正确的前缀 属性 名称并且不包括用于绑定到集合的索引器。例如,如果集合 属性 被命名为 FurnitureItems,那么 typeof BistroReservations_ReservationFurnitureQuantity 属性 所需的 html 需要是

<input type="text" name="FurnitureItems[0].Quantity" ...>
<input type="text" name="FurnitureItems[1].Quantity" ...>

您可以使用 BeginCollectionItem 助手生成控件,它可能看起来像(假设 属性 被命名为 FurnitureItems

@model CdvPortal.Models.BistroReservations.BistroReservations_ReservationFurniture
@using (Html.BeginCollectionItem("FurnitureItems"))
{
  ....
  @Html.LabelFor(m => m.Quantity, ..)
  @Html.EditorFor(m => m.Quantity, ..)
  @Html.ValidationMessageFor(m => m.Quantity, ..)
}

这将生成正确的前缀并添加一个基于 guid 的索引器,它还允许您从集合中删除项目。 This article 更详细地解释了用法

中显示了纯客户端替代方案。这提供了更好的性能,但更难维护,因为更改 BistroReservations_ReservationFurniture 模型中的任何内容都意味着更新客户端模板。