Asp.net mvc 带ViewBag的MVC DropDownListFor未将值传递给控制器

Asp.net mvc 带ViewBag的MVC DropDownListFor未将值传递给控制器,asp.net-mvc,entity-framework,viewbag,dropdownlistfor,Asp.net Mvc,Entity Framework,Viewbag,Dropdownlistfor,我很难在create视图中设置相关模型/表的下拉列表。My Guest模型以以下方式引用PersonPrefix模型: 嘉宾模式: public virtual PersonPrefix Prefix { get; set; } PersonPrefix模型: public class PersonPrefix { [Key] public int PersonPrefixID { get; set; } [StringLength(6)] public st

我很难在create视图中设置相关模型/表的下拉列表。My 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; }

}
我已经在帖子中添加了前缀对象

[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);
}
这是迄今为止视图中的相关代码,但未将值传递给控制器:

public ActionResult Create()
{
    ViewBag.PersonPrefixes = new SelectList(db.PersonPrefixes, "PersonPrefixID", "Abbreviation");
    return View();
} 
  <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>

@LabelFor(model=>model.Prefix,htmlAttributes:new{@class=“controllabel col-md-2”})
@*@DropDownListFor(m=>m.前缀,新选择列表(ViewBag.PersonPrefixes,“Value”,“Text”,1))*@
@Html.DropDownListFor(m=>m.前缀,
新建选择列表(ViewBag.Person前缀,“值”、“文本”,1))
谢谢你的帮助

不能将
元素绑定到复杂对象。所有html表单控件都回发一个值(或者在
值类型数组的情况下)。如果您选择了一个值为(比如)5的选项,则
DefaultModelBinder
将尝试设置
guest.Prefix=5当然失败了

您需要绑定到值类型(例如,
int
string
等)。在您的情况下,您甚至无法绑定到
PersonPrefix
PersonPrefix
,因为对
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
}
看法

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

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
),您试图将所选值设置为
1
的最后一个参数将被忽略(它是您绑定到的属性值,用于确定选择哪个选项)因此,如果您想使用
value=“1”
预先选择一个选项,则设置
SelectedPrefix=1的值

虽然根据Stephen的回答,最好使用ViewModels,但您的错误在于:

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

@Html.DropDownListFor(m => m.Prefix.PersonPrefixID, 
                           (SelectList)ViewBag.PersonPrefixes, "Select...")
第一个参数是从PersonPrefixes中的对象列表中选择的id。。。因此,当模型发布到控制器时,它将包含ID(但不包含其他数据,因此您必须确保查询数据库以填充它)

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

Create([Bind(Include = "GuestID,FirstName,MiddleName,Surname,BirthDate")] Guest guest)
然后在PersonPrefix类上使用绑定语法:

[Bind(Include = "PersonPrefixID")]
public class PersonPrefix
{
    [Key]
    public int PersonPrefixID { get; set; }
    .....
}
现在,当您发布时,PersonPrefix的其余部分将为空,因为此数据不在发布中,因此您必须使用发布的PersonPrefix从数据库中检索它


但是,正如我所说,最好使用ViewModels。即使如此,我在这里说的一些同样的话仍然适用。您的DropDownListFor需要正确,并使用选定的id属性。而且,如果您使用的是ViewModel,则没有理由使用绑定属性,因为您只绑定所需的确切项目。

您无法将
绑定到复杂对象(这就是
前缀
的属性)。在您的情况下,您需要使用具有属性(例如)的视图模型
int SelectedPrefix
,并绑定到该属性。谢谢!我是MVC新手,你能给我举个好例子吗?看看这个例子:谢谢你的解释!不过,在我实施之前还有一个问题:最佳实践是什么?我是否应该将来宾模型更改为包含public int PersonPrefixId(EF是否仍然能够像那样处理代码优先迁移?),还是应该在模型中保留复杂对象引用并构建viewmodels?最佳实践始终是使用视图模型。它有许多优点,例如将指定显示和验证属性应用于属性,防止过度发布攻击,避免使用
ViewBag
等(另请参阅。还有一些工具,例如,使数据模型和视图模型之间的映射更容易。您可以尝试一下,并让您知道它是如何进行的:)好的,我就快到了。我只是不明白在POST方法中该怎么做?当前看起来是这样的:[HttpPost][ValidateAntiForgeryToken]公共操作结果创建([Bind(Include=“GuestID,FirstName,MiddleName,姓氏,生日,SelectedPrefix”)]来宾来宾来宾){if(ModelState.IsValid){db.Guests.Add(Guest);db.SaveChanges();return RedirectToAction(“Index”);}return View(guest);}在评论中太难阅读(建议您使用新代码编辑您的问题),但是POST方法参数需要是
GuestVM
(而不是
guest
),并且
[Bind]
属性不是必需的。给我5分钟,我将在POST方法中用更多的代码编辑我的答案,以使其更清晰。一个潜在的问题是
PersonPrefix
的其他属性具有验证属性,因此
ModelState
在回发时将无效-这意味着必须在回发之前删除这些属性上的
ModelState
错误测试
ModelState
是否有效(因此是否应返回视图进行更正)@StephenMuecke-只有StringLength,这是最大长度。。如果你不去约束它,它就不可能失败
[Bind(Include = "PersonPrefixID")]
public class PersonPrefix
{
    [Key]
    public int PersonPrefixID { get; set; }
    .....
}