自定义模型绑定器来绑定嵌套属性值

问题描述:

我需要这个的原因:在我的一个控制器中,我想以不同于应用程序其余部分的方式绑定所有Decimal值。我不想注册模型绑定在Global.asax中(通过ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());自定义模型绑定器来绑定嵌套属性值

我试图从DefaultModelBinder类派生并覆盖其BindProperty方法,但只适用于模型实例的直接(不嵌套)十进制属性。

我有以下的例子来说明我的问题:

namespace ModelBinderTest.Controllers 
{ 
    public class Model 
    { 
     public decimal Decimal { get; set; } 
     public DecimalContainer DecimalContainer { get; set; } 
    } 

    public class DecimalContainer 
    { 
     public decimal DecimalNested { get; set; } 
    } 

    public class DecimalModelBinder : DefaultModelBinder 
    { 
     protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
     { 
      if (propertyDescriptor.PropertyType == typeof (decimal)) 
      {     
       propertyDescriptor.SetValue(bindingContext.Model, 999M); 
       return; 
      } 

      base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
     } 
    } 

    public class TestController : Controller 
    { 

     public ActionResult Index() 
     { 
      Model model = new Model(); 
      return View(model); 
     } 

     [HttpPost] 
     public ActionResult Index([ModelBinder(typeof(DecimalModelBinder))] Model model) 
     { 
      return View(model); 
     } 

    } 
} 

该解决方案只设置ModelDecimal属性为999,但没有做任何事情来DecimalContainerDecimalNested财产。我意识到这是因为在我的DecimalModelBinderBindProperty重写中调用base.BindProperty,但我不知道如何说服基类在处理小数属性时使用我的模型活页夹。

你可以无条件申请模型绑定在你的Application_Start

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder()); 

,然后有一个自定义的授权过滤器(是的,授权过滤器,因为它模型绑定之前运行),将注入的HttpContext一定的价值可稍后通过模型绑定使用:

[AttributeUsage(AttributeTargets.Method)] 
public class MyDecimalBinderAttribute : ActionFilterAttribute, IAuthorizationFilter 
{ 
    public void OnAuthorization(AuthorizationContext filterContext) 
    { 
     filterContext.HttpContext.Items["_apply_decimal_binder_"] = true; 
    } 
} 

,然后在模型绑定测试是否含有HttpContext的应用befoire它的自定义值:

public class DecimalModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     if (controllerContext.HttpContext.Items.Contains("_apply_decimal_binder_")) 
     { 
      // The controller action was decorated with the [MyDecimalBinder] 
      // so we can proceed 
      return 999M; 
     } 

     // fallback to the default binder 
     return base.BindModel(controllerContext, bindingContext); 
    } 
} 

现在,所有剩下的就是装饰用自定义过滤器的控制器动作,使小数粘结剂:

[HttpPost] 
[MyDecimalBinder] 
public ActionResult Index(Model model) 
{ 
    return View(model); 
}