POST到MVC控制器当我没有按顺序选中复选框时,IEnumerable嵌套模型为null

Art*_*r B 3 c# asp.net-mvc razor visual-studio-2013 asp.net-mvc-5

当我试图在Post中获取值时,当我不按顺序(1,2,3等)检查时,复选框的值被设置为NULL.

我需要在任何顺序中选择它们中的任何一个(即4,5).

模型:

public class AssignUsersViewModel
{
    [Display(Name = "Check to select")]
    public bool Select { get; set; }

    public int Id { get; set; }

    [Display(Name = "App. Username")]
    public string UserName { get; set; }

    [Required]
    public string GivenName { get; set; }

    [Required]
    public string Surname { get; set; }

    [Display(Name = "Roles")]
    public IList<Roles> Roles { get; set; }
}

public class AssignUsersAddModel
{
    public bool Select { get; set; }

    public int Id { get; set; }

    public IEnumerable<SelectedRoles> selectedRoles { get; set; }
}

public class SelectedRoles
{
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

CSHTML:

@model IList<AspNetIdentity2DRH.Models.AssignUsersViewModel>
@using (Html.BeginForm("UsersAddToApp", "UsersAdmin", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    <table class="table">
        <tr>
            <th>Check for add</th>
            <th>Username</th>
            <th>Givenname</th>
            <th>Surename</th>
            <th>Roles</th>
        </tr>
        @for (int i = 0; i < Model.Count(); i++)
        {
            <tr>
                <td>
                    @Html.CheckBoxFor(x => x[i].Select)
                    @Html.HiddenFor(x => x[i].Id)
                </td>
                <td>
                    @Html.DisplayFor(x => x[i].UserName)
                </td>
                <td>
                    @Html.DisplayFor(x => x[i].GivenName)
                </td>
                <td>
                    @Html.DisplayFor(x => x[i].Surname)
                </td>
                <td>
                    <div class="row">
                        <div class="form-group">
                            @for (int j = 0; j < Model[i].Roles.Count(); j++)
                            { 
                                <div class="col-sm-4">
                                    <input type="checkbox" name="[@i.ToString()].selectedRoles[@j.ToString()].Name" value="@Model[i].Roles[j].Name" class="checkbox-inline" />
                                    @Html.Label(Model[i].Roles[j].Name, new { @class = "control-label", @data_toggle = "tooltip", @data_placement = "top", @data_original_title = Model[i].Roles[j].Description })
                                </div>
                            }
                        </div>
                    </div>
                </td>
            </tr>
        }
    </table>
    <p>
        <input type="submit" value="Add existing user" class="btn btn-primary" />
        <input type="button" value="Cancel" onclick="window.location.href = '@Url.Action("UsersIndex", "UsersAdmin")';" class="btn btn-cancel" />
    </p>
}
Run Code Online (Sandbox Code Playgroud)

控制器:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UsersAddToApp(List<AssignUsersAddModel> model)
{
    if (ModelState.IsValid)
    {
        foreach (AssignUsersAddModel item in model)
        {
            if (item.Select)
            {
                using (DbContextTransaction dbtrans = db.Database.BeginTransaction())
                {
                    try
                    {
                        int appId = (int)Session["ApplicationId"];
                        Users user = UserManager.FindById(item.Id);
                        db.ApplicationUsers.Add(new ApplicationUsers { ApplicationId = appId, UserId = user.Id });
                        db.SaveChanges();
                        foreach (SelectedRoles RolesItem in item.selectedRoles)
                        {
                            int roleId = db.Roles.Where(r => r.Name == RolesItem.Name).Select(r => r.Id).FirstOrDefault();
                            db.UserApplicationRoles.Add(new UserApplicationRoles { ApplicationId = appId, UserId = user.Id, RoleId = roleId });
                            db.SaveChanges();
                        }
                        dbtrans.Commit();
                    }
                    catch (Exception)
                    {
                        dbtrans.Rollback();
                    }
                }

            }
        }
        return RedirectToAction("UsersAddToApp");
    }
    ModelState.AddModelError("", "An error has occurred. Contact system administrator.");
    return RedirectToAction("UsersAddToApp");
}
Run Code Online (Sandbox Code Playgroud)

问题是当我选择复选框时(除第一个之外的所有复选框,或者中间的最后一个复选框,该行:

foreach (SelectedRoles RolesItem in item.selectedRoles)
Run Code Online (Sandbox Code Playgroud)

发送item.selectedRoles为null.

我怎么能做到这一点?

小智 5

DefaultModelBinder只结合藏品在收藏项的索引以零开始,是连续的.您的问题是您手动创建复选框元素.由于未选中的复选框不会回发,如果您取消选中,则在提交时,它和任何后续复选框值都不会绑定到集合.

接下来,您尝试将复选框绑定到string值.复选框有2个状态,用于表示布尔值.

您没有显示您的Role(视图)模型,但它应该包含一个boolean属性,指示它是否已被选中

public class Role
{
  public int ID { get; set; }
  public string Name { get; set; }
  public bool IsSelected { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后在视图中,使用强类型html帮助程序,以便获得正确的2路模型绑定

@for (int j = 0; j < Model[i].Roles.Count; j++)
{ 
  @Html.HiddenFor(m => m[i].Roles[j].Name) // or better use the ID property
  @Html.CheckBoxFor(m => m[i].Roles[j].IsSelected)
  @Html.LabelFor(m => m[i].Roles[j].IsSelected, Model[i].Roles[j].Name)
}
Run Code Online (Sandbox Code Playgroud)

然后在POST方法中,您的集合将被正确绑定,您可以使用say访问所选角色,

List<string> selectedRoles = model.Roles.Where(r => r.IsSelected);
Run Code Online (Sandbox Code Playgroud)

请注意,您可能还希望包含Role.ID(而不是Role.Name属性)的隐藏输入,因此您不需要在POST方法foreach循环中执行数据库查找.

附注:您的帖子方法需要

public ActionResult UsersAddToApp(List<AssignUsersViewModel> model)
Run Code Online (Sandbox Code Playgroud)

ListAssignUsersAddModel> model