C#MVC-发布包含字典的viewmodel

时间:2016-06-04 13:19:21

标签: c# asp.net-mvc dictionary serialization

我在发布包含字典的viewmodel时遇到问题。每次我在控制器操作中收到模型时,所有值都为空(实际上只是我需要的字典值),而且我不确定如何解决这个问题或者为什么会发生这种情况。您可以在下面找到我的视图(不相关的部分除外),我的控制器和我正在使用的视图模型。这里的想法是传递一个将用户与角色相关联的字典,允许管理员编辑各种用户角色,然后将信息传递回控制器以使用新角色信息更新数据库。我有幸运过过去的序列化IEnumerables(列表等),但不能让它为字典工作。所有帮助表示赞赏。

观点:

@model RecipeManager.ViewModels.AdminViewModel

@{
    ViewBag.Title = "Index";
}
<h2>Admin</h2>
@using (Html.BeginForm("UpdateRoles", "Admin", FormMethod.Post))
{
    <div>
        @for (int i = 0; i < Model.UsersAndRoles.Count; i++)
        {
            <strong>@Model.UsersAndRoles.ElementAt(i).Key.UserName</strong> 
            @Html.DropDownListFor(m => m.UsersAndRoles.ElementAt(i).Value, Model.RoleTypes, Model.UsersAndRoles.ElementAt(i).Value, new { @class = "form-control" })
        }
    </div>
    <input type="submit" value="Update Roles" />
}

html的示例产生了用户名和管理员角色下拉列表:

<strong>test+cook@test.com</strong> <select class="form-control" id="Value" name="Value"><option value="">Cook</option>
<option value="1">Admin</option>
<option value="2">Cook</option>
<option value="3">Retail</option>
</select>   

Controller Action(在AdminController中):

[AuthorizeUser(Activity = "ViewAdmin")]
[HttpPost]
public ActionResult UpdateRoles(AdminViewModel vm)
{
  return View("Index");
}

ViewModel(AdminViewModel):

namespace RecipeManager.ViewModels
{
  public class AdminViewModel
  {

    public SelectList RoleTypes { get; set; }

    public String SelectedRole { get; set; }

    public String InviteEmailAddress { get; set; }

    public Dictionary<ApplicationUser, string> UsersAndRoles { get; set; }


  }
}

1 个答案:

答案 0 :(得分:2)

您生成的<select>元素的名称属性(name="Value")与您的属性没有关系Dictionary。要绑定到Dictionary以需要KeyValue属性的输入,并且因为它是一个集合,它们也需要被编入索引。在你的情况下,它变得更糟,因为你的Key是一个复杂的对象。

为了绑定,您生成的html必须是

<input type="hidden" name="UsersAndRoles[0].Key.Id" value="...." />
<input type="hidden" name="UsersAndRoles[0].Key.UserName" value="...." />
.... // more hidden inputs for each property of ApplicationUser
<select name="UsersAndRoles[0].Value">
    <option value="">Cook</option>
     ....
</select>

<input type="hidden" name="UsersAndRoles[1].Key.Id" value="...." />
<input type="hidden" name="UsersAndRoles[1].Key.UserName" value="...." />
.... // more hidden inputs for each property of ApplicationUser
<select name="UsersAndRoles[1].Value">
    <option value="">Cook</option>
     ....
</select>

标准HtmlHelper方法不会开箱即用(尽管您可以创建自己的扩展方法来生成html)。但是,如果您只是设计视图模型来表示要在视图中编辑的内容,那将会容易得多。

然而,从您当前的视图模型中不清楚您想要绑定到什么。 您有public String SelectedRole { get; set; }属性未使用且Dictionary Value类型为string,但SelectListItem的值为int类型。此外,使用DropDownListFor()方法将无法正常运行,除非您将EditorTemplateAdditionalViewData结合使用,或生成this answer中所述的新IEnumerable<SelectListItem>。 (请注意,即使第3个选项(“cook”)应该是),也没有任何选项具有selected属性。另请注意,DropDownListFor()的第3个参数会生成null选项,因此它应为"Please select"或类似,而不是当前角色的值。

您的视图模型(基于您显示的视图代码)应为

public class AdminViewModel
{
    public SelectList RoleTypes { get; set; }
    public List<UserRoleViewModel> UserRoles { get; set; }
}
public class UserRoleViewModel
{
    public int ID { get; set; }
    public string UserName { get; set; }
    [Display(Name = "Role")]
    [Required(ErrorMesage = "Please select a role")]
    public int SelectedRole { get; set; }
}

然后在EditorTemplate

中创建/Views/Shared/EditorTemplates/UserRoleViewModel.cshtml
@model UserRoleViewModel
@Html.HiddenFor(m => m.ID)
@Html.HiddenFor(m => m.UserName)
<strong>@Html.DisplayFor(m => m.UserName)</strong>
@Html.LabelFor(m => m.SelectedRole)
@Html.DropDownListFor(m => m.SelectedRole, (SelectList)ViewData["RoleTypes"], "Please Select")
@Html.ValidationMessageFor(m => m.SelectedRole)

然后在主视图中

@model RecipeManager.ViewModels.AdminViewModel
....
@using (Html.BeginForm("UpdateRoles", "Admin", FormMethod.Post))
{
    @Html.EditorFor(m => m.UserRoles, new { RoleTypes = Model.RoleTypes })
    <input type="submit" value="Update Roles" />
}