古老的问题,但我想我还是会为后代回答。在这种情况下,您需要一个自定义模型绑定器来拦截该属性,然后再尝试绑定它。默认模型绑定器将递归尝试使用其自定义绑定器绑定属性,如果未设置,则使用默认绑定器。
您在 DefaultModelBinder 中寻找的覆盖是 GetPropertyValue。这会在模型中的所有属性上调用,默认情况下它会回调 DefaultModelBinder.BindModel - 整个过程的入口点。
简化模型:
public class Organization
{
public int Id { get; set; }
[Required]
public OrganizationType Type { get; set; }
}
public class OrganizationType
{
public int Id { get; set; }
[Required, MaxLength(30)]
public string Name { get; set; }
}
查看:
<div class="editor-label">
@Html.ErrorLabelFor(m => m.Organization.Type)
</div>
<div class="editor-field">
@Html.DropDownListFor(m => m.Organization.Type.Id, Model.OrganizationTypes, "-- Type")
</div>
模型绑定器:
public class OrganizationModelBinder : DefaultModelBinder
{
protected override object GetPropertyValue(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
System.ComponentModel.PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
if (propertyDescriptor.PropertyType == typeof(OrganizationType))
{
var idResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Id");
if (idResult == null || string.IsNullOrEmpty(idResult.AttemptedValue))
{
return null;
}
var id = (int)idResult.ConvertTo(typeof(int));
// Can validate the id against your database at this point if needed...
// Here we just create a stub object, skipping the model binding and
// validation on OrganizationType
return new OrganizationType { Id = id };
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}
请注意,在视图中,我们为 model.foo.bar.Id 创建了 DropDownList。在模型绑定器中,确保也将其添加到模型名称中。您可以不使用两者,但是 DropDownListFor 在查找所选值时会遇到一些问题,而无需在您发送的 SelectList 中预先选择它。
最后,回到控制器,确保在你的数据库上下文中附加这个属性(如果你使用的是实体框架,其他人可能会以不同的方式处理)。否则,它不会被跟踪,并且上下文将尝试在保存时添加它。
控制器:
public ActionResult Create(Organization organization)
{
if (ModelState.IsValid)
{
_context.OrganizationTypes.Attach(organization.Type);
_context.Organizations.Add(organization);
_context.SaveChanges();
return RedirectToAction("Index");
}
// Build up view model...
return View(viewModel);
}