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

时间:2011-04-14 02:12:11

标签: c# asp.net-mvc-3 drop-down-menu model-binding

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

标记看起来像这样:

<div class="editor-label">
    @Html.LabelFor(model => model.FileType)
</div>
<div class="editor-field">
    @Html.DropDownListFor(model => model.FileType.Variety, (SelectList)ViewBag.FileTypes)
</div>

<div class="editor-label">
    @Html.LabelFor(model => model.Status)
</div>
<div class="editor-field">
    @Html.DropDownListFor(model => model.Status.Status, (SelectList)ViewBag.Status)
</div>

我的控制器动作如下:

[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对象,但这意味着要更改当前期望强类型模型参数的单元测试的重要部分。

2 个答案:

答案 0 :(得分:3)

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

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

public class LookupModelBinder<TModel> : 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<TModel>(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<EmploymentType>();

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

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

答案 1 :(得分:2)

请改为尝试:

<div class="editor-label">
    @Html.LabelFor(model => model.FileType)
</div>
<div class="editor-field">
    @Html.DropDownListFor(model => model.FileType.Id, (SelectList)ViewBag.FileTypes)
</div>

<div class="editor-label">
    @Html.LabelFor(model => model.Status)
</div>
<div class="editor-field">
    @Html.DropDownListFor(model => model.Status.Id, (SelectList)ViewBag.Status)
</div>