ASP.NET MVC3 JSON decimal binding woes
I just stumbled onto this bug the other day and I figured I would share. Given the following controller action and ajax call, what would you expect the value of deez to be in the BreakStuff action?
//Model to be bound.
public class Decimals {
public decimal d1 { get; set; }
public decimal d2 { get; set; }
public decimal d3 { get; set; }
}
//Controller action to check the binding
public ActionResult BreakStuff(Decimals deez) {
return new EmptyResult();
}
//Ajax call to controller action above
$.ajax({
type: "POST",
url: "/Home/BreakStuff",
contentType:'application/json',
data:JSON.stringify({d1:42.00,d2:3.14,d3:'17'})
});
A screenshot says a thousand words:
What happened to d1? The resulting JSON from JSON.stringify({d1:42.00,d2:3.14,d3:'17'}) ends up being {"d1":42,"d2":3.14,"d3":"17"} Cool right? Mostly, except that once this value gets server side, that 42.00 is now seen as an int (42) and the default model binder doesn't want to stuff that into my decimal type. Bummer. Not exactly what I was expecting.
Phil Haack to the rescue! After doing some searching I stumbled across this post which fixes binding to string values with comma separators. It solves the same problem I'm having, so I'll repost the bits here:
using System;
using System.Globalization;
using System.Web.Mvc;
public class DecimalModelBinder : IModelBinder {
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
ValueProviderResult valueResult = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName);
ModelState modelState = new ModelState { Value = valueResult };
object actualValue = null;
try {
actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
CultureInfo.CurrentCulture);
}
catch (FormatException e) {
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
Then stick this into your Global.asax.cs Application_Start:
protected void Application_Start() {
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
// Your other stuff goes here.
}
Once I did that and re-ran the code above, everything works as expected. See for yourself:
Comments(7)
