Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC3 Model binding causes "The parameter conversion from type 'System.Int32' to 'System.Decimal' failed - no type converter"

I'm getting the following exception:

Exception {"The parameter conversion from type 'System.Int32' to type 'System.Decimal' failed because no type converter can convert between these types."} System.Exception {System.InvalidOperationException}

This is after I use JQuery Ajax post to post the json back to the controller. MVC3 is binding the JSON to the model correctly as I can see all the data in a watch, however the ModelState has this error.

The View has a single decimal field and a textbox which holds a number. I get this error even when the textbox has an integer value.

Any ideas as to why this is failing?

like image 791
jaffa Avatar asked Mar 31 '11 12:03

jaffa


2 Answers

The problem seems to stem from the default Model Binder which comes with MVC3 being unable to convert an integer to a decimal. It can however, convert if the source value in the json is a string or decimal value.

The solution is to create a custom model binder for decimal values.

Add this to the global.asax.cs

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

And create the model binder:

  public class DecimalModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            return valueProviderResult == null ? base.BindModel(controllerContext, bindingContext) : Convert.ToDecimal(valueProviderResult.AttemptedValue);
        }
    }
like image 151
jaffa Avatar answered Sep 22 '22 05:09

jaffa


To slightly improve jaffa's great answer, you may want to use Decimal.TryParse so that unconvertible values such as an empty string do not throw exceptions but are handed on to the base binder to be handled in a consistent way.

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        decimal value;
        return valueProviderResult == null || !Decimal.TryParse(valueProviderResult.AttemptedValue, out value) ? base.BindModel(controllerContext, bindingContext) : value;

    }

As far as I can see, the original failure is that of the ValueProviderResult not providing a converter, which internally comes from the TypeDescriptor failing to provide a suitable converter. At this point I stopped looking :)

Also remember to handle Nullable decimals as well:

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
like image 34
Simon Francesco Avatar answered Sep 19 '22 05:09

Simon Francesco