带有 ViewBag 的 MVC DropDownListFor 未将值传递给控制器

MVC DropDownListFor with ViewBag not passing value to controller

我很难尝试在我的创建视图中为相关 model/table 设置下拉列表。我的 Guest 模型通过以下方式引用了 PersonPrefix 模型:

客座模特:

public virtual PersonPrefix Prefix { get; set; }

PersonPrefix 模型:

public class PersonPrefix
{

    [Key]
    public int PersonPrefixID { get; set; }
    [StringLength(6)]
    public string Abbreviation { get; set; }
    [StringLength(255)]
    public string Name { get; set; }
    public virtual ICollection<Guest> Guests { get; set; }

}

为了能够从数据库中获取数据并将其显示在下拉列表中,我已经完成了以下操作:

控制器:

public ActionResult Create()
{
    ViewBag.PersonPrefixes = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
    return View();
} 

并且我已将前缀对象添加到 post

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "GuestID,FirstName,MiddleName,Surname,BirthDate,SelectedPrefix")] Guest guest)
{
    if (ModelState.IsValid)
    {
        db.Guests.Add(guest);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(guest);
}

到目前为止,这是视图中的相关代码,但它没有将值传递给控制器​​:

  <div class="form-group">
        @Html.LabelFor(model => model.Prefix, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10" >
            @*@Html.DropDownListFor(m => m.Prefix,  new SelectList(ViewBag.PersonPrefixes, "Value", "Text", 1))*@
            @Html.DropDownListFor(m => m.Prefix,
            new SelectList(ViewBag.PersonPrefixes, "Value", "Text", 1))

        </div>
    </div>

感谢您的帮助!!

您不能将 <select> 元素绑定到复杂对象。所有 html 表单控件 post 返回单个值(或者在 <select multiple> 的情况下返回值类型数组)。如果您 select 编辑了一个值为(比如)5 的选项,那么 DefaultModelBinder 将尝试设置 guest.Prefix = 5; 这当然会失败。

您需要绑定到一个值类型(例如 intstring 等)。在您的情况下,您甚至不能绑定到 PersonPrefixPersonPrefixID,因为验证会在 PersonPrefix 的其他属性上失败。与往常一样,在编辑时,您应该使用仅包含需要编辑的那些属性的视图模型。

public class GuestVM
{
  [Display(Name = "Prefix")]
  [Required(ErrorMessage = "Please select a prefix")]
  public int SelectedPrefix { get; set; }
  .... // other properties of Guest
  public SelectList PrefixList { get; set; }
}

控制器

public ActionResult Create()
{
  GuestVM model = new GuestVM();
  model.PrefixList = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
  .... // set other properties as required
  return View(model); // always return an instance of the model
}

查看

@Html.LabelFor(m => m.SelectedPrefix)
@Html.DropDownListFor(m => m.SelectedPrefix, Model.PrefixList, "--please select--")
@Html.ValidationMessageFor(m => m.SelectedPrefix)

然后在POST方法中,初始化Guest数据模型的新实例,并从posted视图模型映射其属性,最后保存数据模型。

public ActionResult Create(GuestVM model)
{
  if (!ModelSTate.IsValid)
  {
    model.PrefixList = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
    return View(model);
  }
  // Initialize a new instance of the data model and set its properties
  Guest guest = new Guest()
  {
    FirstName = model.FirstName,
    MiddleName = model.MiddleName,
    .... // other properties
    Prefix = db.PersonPrefixes.Find(model.SelectedPrefix)
  };
  db.Guests.Add(guest);
  db.SaveChanges();
  return RedirectToAction("Index");
}

旁注:您不需要在视图中创建另一个 SelectList(它已经是 SelectList)和您尝试设置 selected 值的最后一个参数to 1 被忽略(它的 属性 的值决定了哪个选项是 selected)所以如果你想预先 select 一个带有 value="1",然后在将模型传递给视图之前,在控制器中设置 SelectedPrefix = 1; 的值。

虽然根据 Stephen 的回答,最好使用 ViewModels,但您在这里具体做错的是:

首先,你的DropDownListFor是错误的。您的 ViewBag 条目已包含一个 SelectList,因此您无需再创建一个...您只需转换动态类型即可。

@Html.DropDownListFor(m => m.Prefix.PersonPrefixID, 
                           (SelectList)ViewBag.PersonPrefixes, "Select...")

第一个参数是从 PersonPrefixes 中的对象列表中选择的 ID...因此当模型 posted 到控制器时,它将包含 ID(但不包含其他数据,因此您必须确保查询数据库以填充它。

其次,您需要将 Post 操作更改为:

Create([Bind(Include = "GuestID,FirstName,MiddleName,Surname,BirthDate")] Guest guest)

然后在您的 PersonPrefix class 上使用绑定语法:

[Bind(Include = "PersonPrefixID")]
public class PersonPrefix
{
    [Key]
    public int PersonPrefixID { get; set; }
    .....
}

现在,当您 post 时,PersonPrefix 的其余部分将为空,因为此数据不存在于 post 中,因此您必须使用 [=] 从数据库中检索它29=]ed PersonPrefixID.

但是,正如我所说,最好使用 ViewModel。即便如此,我在这里所说的某些内容仍然适用。您的 DropDownListFor 需要正确,并使用选定的 ID 属性。而且,如果您使用的是 ViewModel,则没有理由使用 Bind 属性,因为您只是绑定了您需要的确切项。