1

I've created a custom model binder based on an article from Haacked. Here's the code:

namespace MyNamespace.Project.ModelBinders
{
    public class DecimalModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            string modelName = bindingContext.ModelName;
            ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(modelName);

            ModelState modelState = new ModelState { Value = valueResult };

            object actualValue = null;

            try
            {
                //replace commas with periods
                actualValue = Convert.ToDecimal(valueResult.AttemptedValue.Replace(",", "."));
            }
            catch (Exception ex)
            {
                modelState.Errors.Add(ex);
            }

            bindingContext.ModelState.Add(modelName, modelState);

            return actualValue;
        }
    }
} 

When MVC loads a view where the controller action is something like this:

public ActionResult Index(decimal amount)  

It seems to fire the model binding and add the error and this is because amount at this point is null because I have a valid use case where index can be loaded with or without parameters (QueryString). As far as I know MVC doesn't support typical OO method overloading such that you have:

public ActionResult Index(decimal amount) {}  
public ActionResult Index() {}  

So, would it be valid to add a null check to my custom model binder to avoid the error that is raised in the try block, or would this interfere with validation?

Jacques
  • 6,936
  • 8
  • 43
  • 102
  • How about a nullable parameter, e.g. `decimal? amount` – DavidG Dec 05 '17 at 15:30
  • This code caters for nullable decimals too (registered in Global.asax) the main point is to allow the model binder to handle decimals where commas and periods are used as the separator. But, it's more about my understanding of custom model binders – Jacques Dec 05 '17 at 15:36

1 Answers1

1

I see multiple points here in general. First one is regarding this:

As far as I know MVC doesn't support typical OO method overloading...

That's right, but it has a very flexible routes configuration to help with such problems.

You could configure separate routes for calls having the parameter or not having one. I didn't try that, but this would be a sample using Attribute Routing.

[Route("index/{amount}"]
public ActionResult IndexWithAmount(decimal amount) {}  

[Route("index")]
public ActionResult Index() {}

Another thing you can do, as described here is to not use the Model Binder globally but rather enable it only on specific routes:

public ActionResult Index(
    [ModelBinder(typeof(DecimalModelBinder))]decimal amount) {}
thmshd
  • 5,729
  • 3
  • 39
  • 67
  • How would you apply this level of model binding if your parameter is an object with a decimal property? public ActionResult Index(MyObject myObjectWithDecimalProperty) – Jacques Dec 06 '17 at 06:52
  • What you probably want to do is introduce a `PropertyBinder` Attribute, this is quite old but probably still valid: http://aboutcode.net/2011/03/12/mvc-property-binder.html – thmshd Dec 06 '17 at 09:03
  • Update to my previous comment: This SO Answer is based on the code from the link above, but might be even better for your case, as it really does this kind of Conversion (but not to decimal but to TimeSpan): https://stackoverflow.com/a/12683210/265165 – thmshd Dec 06 '17 at 09:06