如何防止模型绑定器中的关系验证?

时间:2010-07-16 11:14:57

标签: asp.net-mvc-2 modelbinders

一个例子,如果我有一个名为Order的类,其中一个字段引用一个Customer,然后一个Order表单带有一个下拉列表(<%= Html.DropDownListFor(e => e.Customer.ID,new) SelectList(...))%>)用于设置Customer,模型绑定器将创建一个仅设置了ID的空Customer。这适用于NHibernate,但是当验证添加到Customer类的某些字段时,模型绑定器会说这些字段是必需的。如何防止模型绑定器验证这些引用?

谢谢!

1 个答案:

答案 0 :(得分:0)

年老的问题,但我想我总是回答后人的问题。在这种情况下,您需要一个自定义模型绑定程序来拦截该属性,然后再尝试绑定它。默认模型绑定器将递归尝试使用其自定义绑定器绑定属性,如果未设置则默认绑定属性。

您在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);
    }
}

请注意,在View中我们为model.foo.bar创建DropDownList。 Id 。在模型绑定器中,确保将其添加到模型名称中。您可以将其从两者中删除,但DropDownListFor在找到所选值时遇到一些问题,而无需在您发送的SelectList中预先选择它。

最后,回到控制器中,确保在数据库上下文中附加此属性(如果您使用的是Entity Framework,其他人可能会以不同的方式处理)。否则,它不会被跟踪,上下文将尝试在保存时添加它。

<强>控制器:

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);
}