2

The reason I need this: In one of my controllers I want to bind all Decimal values in a different way than the rest of my application. I do not want to register a Model Binder in Global.asax (via ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());)

I have tried deriving from the DefaultModelBinder class and override its BindProperty method, but that only works for the model instance's immediate (not nested) Decimal properties.

I have the following example to demonstrate my problem:

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

    }
}

This solution only sets the Model's Decimal property to 999, but doesn't do anything to DecimalContainer's DecimalNested property. I realize this is because base.BindProperty is called in my DecimalModelBinder's BindProperty override, but I don't know how to convince the base class to use my Model Binder when dealing with decimal properties.

4

1 回答 1

2

您可以在您的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 是否在应用它之前包含自定义值:

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);
}
于 2012-02-24T17:45:12.640 回答