Custom Binder to bind nested property values

The reason I need it: 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 the Binder model in Global.asax (via ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder()); )

I tried to get the BindProperty class from the class and override its BindProperty method, but this only works for the model instance of the immediate (non-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 Decimal property to 999, but does nothing for the DecimalContainer DecimalNested property. I understand that this is because base.BindProperty is called in my override of DecimalModelBinder BindProperty , but I don’t know how to convince the base class to use my model binding when working with decimal properties.

+2
source share
1 answer

You can correctly apply model binding in your Application_Start :

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

and then have a custom authorization filter (yes, an authorization filter that is executed before the model is bound), which will enter some value into the HttpContext that can later be used by the model’s middleware:

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

and then in a test binder test if the HttpContext contains a custom value using it:

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

Now all that remains is to decorate the action of your controller with a special filter to enable the decimal binder:

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

Source: https://habr.com/ru/post/1494362/


All Articles