问题
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.
回答1:
You could apply the model binder unconditionally in your Application_Start
:
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
and then have a custom authorization filter (yes, authorization filter as it runs before the model binder) that will inject into the HttpContext some value that could later be used by the model binder:
[AttributeUsage(AttributeTargets.Method)]
public class MyDecimalBinderAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
filterContext.HttpContext.Items["_apply_decimal_binder_"] = true;
}
}
and then in your model binder test if the HttpContext contains the custom value befoire applying 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's left is to decorate your controller action with the custom filter to enable the decimal binder:
[HttpPost]
[MyDecimalBinder]
public ActionResult Index(Model model)
{
return View(model);
}
来源:https://stackoverflow.com/questions/9434848/custom-model-binder-to-bind-nested-property-values