发布到模型的MVC DropDownList值未绑定

DropDownLists可能是我最不喜欢使用MVC框架的部分。 我的表单中有几个下拉列表,我需要将选定的值传递给ActionResult,该ActionResult接受模型作为其参数。

标记看起来像这样:

@Html.LabelFor(model => model.FileType)
@Html.DropDownListFor(model => model.FileType.Variety, (SelectList)ViewBag.FileTypes)
@Html.LabelFor(model => model.Status)
@Html.DropDownListFor(model => model.Status.Status, (SelectList)ViewBag.Status)

我的控制器动作如下所示:

 [HttpPost] public ActionResult Create(int reviewid, ReviewedFile file) { if (ModelState.IsValid) { UpdateModel(file); } //repository.Add(file); return RedirectToAction("Files", "Reviews", new { reviewid = reviewid, id = file.ReviewedFileId }); } 

这应该都很好,除了下拉的值被发布为null。 当我进一步研究ModelState错误时,发现原因是:

从类型’System.String’到类型’PeerCodeReview.Models.OutcomeStatus’的参数转换失败,因为没有类型转换器可以在这些类型之间进行转换。

它应该不是这么难,但确实如此。 所以问题是; 为了正确绑定模型属性,我需要做什么?

顺便说一句,我知道我可以传入一个FormCollection对象,但这意味着更改我目前期望强类型模型参数的unit testing的重要部分。

试试这个:

 
@Html.LabelFor(model => model.FileType)
@Html.DropDownListFor(model => model.FileType.Id, (SelectList)ViewBag.FileTypes)
@Html.LabelFor(model => model.Status)
@Html.DropDownListFor(model => model.Status.Id, (SelectList)ViewBag.Status)

您需要为绑定到下拉列表的两个属性创建和注册自定义模型绑定器。

这是我为此目的而构建的模型绑定器的代码:

 public class LookupModelBinder : DefaultModelBinder where TModel : class { private string _key; public LookupModelBinder(string key = null) { _key = key ?? typeof(TModel).Name; } public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var dbSession = ((IControllerWithSession)controllerContext.Controller).DbSession; var modelName = bindingContext.ModelName; TModel model = null; ValueProviderResult vpResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (vpResult != null) { bindingContext.ModelState.SetModelValue(modelName, vpResult); var id = (int?)vpResult.ConvertTo(typeof(int)); model = id == null ? null : dbSession.Get(id.Value); } if (model == null) { ModelValidator requiredValidator = ModelValidatorProviders.Providers.GetValidators(bindingContext.ModelMetadata, controllerContext).Where(v => v.IsRequired).FirstOrDefault(); if (requiredValidator != null) { foreach (ModelValidationResult validationResult in requiredValidator.Validate(bindingContext.Model)) { bindingContext.ModelState.AddModelError(modelName, validationResult.Message); } } } return model; } } 

TModel是下拉框应绑定到的属性的类型。 在我的应用程序中,下拉框提供数据库中对象的Id,因此此模型绑定器获取该ID,从数据库中检索正确的实体,然后返回该实体。 您可能有不同的方法将下拉列表给出的字符串转换为模型的正确实体。

您还需要在Global.asax注册模型绑定器。

 binders[typeof(EmploymentType)] = new LookupModelBinder(); 

这假定下拉列表控件的名称与类型名称相同。 如果没有,您可以将密钥传递给模型绑定器。

 binders[typeof(EmploymentType)] = new LookupModelBinder("ControlName");