Model Binding Decimal Values mvc,, code 0 comments suggest edit

I’m in the beautiful country of Brazil right now (I’ll hopefully blog more about that later) proctoring for the hands-on labs that’s part of the Web Camps agenda.

However, the folks here are keeping me on my toes asking me to give impromptu and deeply advanced demos. It almost feels like a form of performance art as I create brand new demos on the fly. Smile

During this time, several people reported issues binding to a decimal value that prompted me to write a new demo and this blog post.

Let’s look at the scenario. Suppose you have the following class (Jogadoris a soccer player in Portugese):

public class Jogador {
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Salary { get; set; }

And you have two controller actions, one that renders a form used to create a Jogador and another action method that receives the POST request.

public ActionResult Create() {
  // Code inside here is not important
  return View();

public ActionResult Create(Jogador player) {
  // Code inside here is not important
  return View();  

When you type in a value such as 1234567.55 into the Salary field and try to post it, it works fine. But typically, you would want to type it like 1,234,567.55 (or here in Brazil, you would type it as 1.234.567,55).

In that case, the DefaultModelBinder chokes on the value. This is unfortunate because jQuery Validate allows that value just fine. I’ll talk to the rest of my team about whether we should fix this in the next version of ASP.NET MVC, but for now it’s good to know there’s a workaround.

In general, we recommend folks don’t write custom model binders because they’re difficult to get right and they’re rarely needed. The issue I’m discussing in this post might be one of those cases where it’s warranted.

Here’s the code for my DecimalModelBinder. I should probably write one for other decimal types too, but I’m lazy.

WARNING: This is sample code! I haven’t tried to optimize it or test all scenarios. I know it works for direct decimal arguments to action methods as well as decimal properties when binding to complex objects.

using System;
using System.Globalization;
using System.Web.Mvc;

public class DecimalModelBinder : IModelBinder {
    public object BindModel(ControllerContext controllerContext, 
        ModelBindingContext bindingContext) {
        ValueProviderResult valueResult = bindingContext.ValueProvider
        ModelState modelState = new ModelState { Value = valueResult };
        object actualValue = null;
        try {
            actualValue = Convert.ToDecimal(valueResult.AttemptedValue, 
        catch (FormatException e) {

        bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
        return actualValue;

With this in place, you can easily register this in Application_Start within Global.asax.cs.

protected void Application_Start() {
    ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());

    // All that other stuff you usually put in here...

That registers our model binder to only be applied to decimal types, which is good since we wouldn’t want model binding to try and use this model binder when binding any other type.

With this in place, the Salary field will now accept both 1234567.55and 1,234,567.55.

Hope you find this useful. I’ve had a great time in Buenos Aires, Argentina and São Paulo, Brazil. I’ll probably be swamped when I get back home, but I’ll try to make time to write about my time here.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.



69 responses

  1. Avatar for Alaor
    Alaor March 19th, 2011

    I was suffering with this issue past week. I live in Brazil and I was very upset because I couldn't make it work. Thanks for the post. ASP.NET MVC 3 is very nice, but sometimes it looks like the globalization features were put in second place.
    p.s. I hope you can get here in Rio de Janeiro sometime in the future to talk to us.

  2. Avatar for jitendra
    jitendra March 19th, 2011

    I never faced this issue. But will remember your solution if i will face it in future. Thanks for the nice post.

  3. Avatar for Eduardo
    Eduardo March 19th, 2011

    There is another thing.
    Since PCs have a dot as a decimal point in the numeric pad, and calculators and other electronic devices use only the point as a decimal separator, a lot people in Argentina (and in other countries where the decimal separator is the comma) is used to sometimes type the point as a separator.
    I have this function since long time to try to guess the user intentions (have worked great for me)
    (old vb code, not optimized)

    <Extension()> _
    Public Function TryCDec(ByRef texto As String) As Decimal
    If String.IsNullOrEmpty(texto) Then
    Return 0
    Dim SepDecimal As String, SepMiles As String
    texto = Replace(Trim(texto), "$", "")
    texto = Replace(Trim(texto), " ", "")
    If CDbl("3,24") = 324 Then
    SepDecimal = "."
    SepMiles = ","
    SepDecimal = ","
    SepMiles = "."
    End If
    If InStr(texto, SepDecimal) > 0 Then
    If InStr(texto, SepMiles) > 0 Then
    If InStr(texto, SepDecimal) > InStr(texto, SepMiles) Then
    texto = Replace(texto, SepMiles, "")
    texto = Replace(texto, SepDecimal, "")
    texto = Replace(texto, SepMiles, SepDecimal)
    End If
    End If
    texto = Replace(texto, SepMiles, SepDecimal)
    End If
    Return CDec(texto)
    End If
    End Function

  4. Avatar for Guillaume
    Guillaume March 19th, 2011

    Thank you for your sample !
    This work great for server side binding but what about client side validation when typing format like "1 234 567,55" ? it just doesn't work out of the box and it is really painfull.
    do you have a "state of the art" workaround for this too ? we implement something but I'm quite unhappy with it.

  5. Avatar for Mike
    Mike March 19th, 2011

    Why wouldn't you fix it in the version of MVC? This is a common issue and seems like it would be easy to fix in the default model binder.

  6. Avatar for Jes&#250;s L&#243;pez
    Jes&#250;s L&#243;pez March 19th, 2011

    We use custom model binders for decimals and to trim strings. Our default model binder is a trimming model binder:
    public class DefaultCustomBinder : DefaultModelBinder
    protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
    if (value != null && propertyDescriptor.PropertyType == typeof(string))
    value = ((string)value).Trim();
    if ((string)value == string.Empty)
    value = null;
    base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);

    And we use this model binder for decimals:
    public class DecimalBinder : DefaultModelBinder
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    if (bindingContext.ModelType == typeof(decimal) || bindingContext.ModelType == typeof(decimal?))
    return BindDecimal(bindingContext);
    return base.BindModel(controllerContext, bindingContext);
    private object BindDecimal(ModelBindingContext bindingContext)
    ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    if (valueProviderResult == null)
    return null;
    bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
    decimal value;
    string valueAsString = valueProviderResult.AttemptedValue == null ? null : valueProviderResult.AttemptedValue.Trim();
    if (string.IsNullOrEmpty(valueAsString))
    return null;
    if (!decimal.TryParse(valueAsString, NumberStyles.Any, Thread.CurrentThread.CurrentCulture, out value))
    var ex = new InvalidOperationException("Valor no válido", new Exception("Valor no válido", new FormatException("Valor no válido")));
    bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
    return null;
    return bindingContext.ModelType == typeof(decimal) ? value : new decimal?(value);

  7. Avatar for Daniel
    Daniel March 19th, 2011

    Actually jogador means "player" ;)

  8. Avatar for Luis Garcia
    Luis Garcia March 20th, 2011

    Happy trip on the Air Force One.

  9. Avatar for Daniel Kolman
    Daniel Kolman March 20th, 2011

    Well, it is actually a bit more complicated. Culture you are using to convert the string into decimal (CultureInfo.CurrentCulture) can be different than culture in user's browser.
    E.g. user has two keyboards and corresponding culture settings installed - german (with "," as decimal separator) and english (with "." as decimal separator). By switching the keyboard in browser (pressing alt+shift), character written by dot key in numeric keyboard changes. Regardless of your application's culture, browser sends either "123.5" or "123,5", depending on user's current keyboard. But both these values are perfectly valid from the user's point of view.
    So then you have hard choice to make: either you accept both "," and "." as decimal separator (but then you do not allow thousands separator) or you are forcing user to input the number in culture expected by your application (but you are forcing the user to choose correct keyboard for your app).

  10. Avatar for Gabriel
    Gabriel March 20th, 2011

    Hey Phil, nice to meet you at the Brasilian Web Camp, next time come with time to play a soccer game!
    Thanks for coming and be sure to come back, next time you might consider going to Rio de Janeiro, there are lots of cabs there (lol)
    Now for the problem on localization, there's also a problem on the JQuery validation plugin as it validates numbers with a en-US regular expression you can try it out on this link:
    I found this bug on the ms connect site
    on the ms connect site there's StackOverflow link and there's a workaround there:
    I'm yet to test this workaround, But I know this is around JQuery Validation plugin, so I think its relevant.
    Sorry for sending you so many links, but I hope it helps you.
    Sorry for puting you on a hard position, the guys only wanted to enjoy the experience of meeting you the max. We really admire you.
    Thanks for everything.

  11. Avatar for Paul Hiles
    Paul Hiles March 20th, 2011

    In situations which require users to enter numbers in text fields, I personally prefer to use strings for properties on my viewmodel together with a regex validator that checks if the value is valid. That way, I always have complete control over the error message even when someone enters an invalid number which would typically cause the model binder to return a default error message that I have no control over. If you are using resource files for your error messages / regex, there is no need to a custom model binder.

  12. Avatar for Frank van Eykelen
    Frank van Eykelen March 21st, 2011

    I'm working on a website that has two cultures (en-GB and nl-NL) and I don't have any problems with Decimal Model Binding.
    Maybe it's because I set the Thread.CurrentThread.CurrentUICulture and Thread.CurrentThread.CurrentCulture to the culture that is used for the form input?
    When the form is Dutch (nl-NL) they are set to {nl} and {nl-NL}, and when the form is English they are set to {en} and {en-GB}.

  13. Avatar for Alaor
    Alaor March 22nd, 2011

    @Frank van Eykelen: You use the IsValid method in the controllers? Because the problem that he posted about (I think) is that, even if the form is using the correct culture and the jquery client validation is OK, when the IsValid method was called it returned a error because it couldn't bind the given decimal. So, if here you did type, for example, "1,01", the client validation would accept, but you would return to the page because of the server side validation.
    At least this is what was happening to me, and this solution solved my problem.

  14. Avatar for Alaor
    Alaor March 22nd, 2011

    Ooops, the IsValid is a property, not a method.

  15. Avatar for Frank van Eykelen
    Frank van Eykelen March 22nd, 2011

    - Yes, I use ModelState.IsValid in my HttpPost ActionResult.
    - The ActionResult does not have a ValidateInput(false) attribute.
    - In my class ProductMetadata I have this:

    [DisplayName("Prijs excl. BTW")]
    [DisplayFormat(ApplyFormatInEditMode = false, DataFormatString = "{0:C2}")]
    public decimal Price { get; set; }

    The DisplayFormat has - as one expects - no effect on the form post, it's only for displaying the value of the decimal.

  16. Avatar for Frank van Eykelen
    Frank van Eykelen March 22nd, 2011

    Sorry, I thought the problem concerned the decimal separator. I now see it's about the thousands separator. When I use those in a decimal field the validation indeed chokes.

  17. Avatar for Leniel Macaferi
    Leniel Macaferi April 24th, 2011

    Hey Phil,
    I'm having problems with the decimal separator ( , ) when dealing with jQuery unobtrusive validation.
    Check this out if you can:

  18. Avatar for StarTrekRedneck
    StarTrekRedneck May 4th, 2011

    I had the same issue with enums-- when the default model binder recieves an integer, it will not use that value to fill a property that's an enum. >:( I could make an EnumModelBinder class that handles all enums, but I can't just register a model binder for the type "enum". Instead, I would have to register for each enum type there was...
    ModelBinders.Binders.Add( typeof( CustomerTypeEnum), ...);
    ModelBinders.Binders.Add( typeof( ProductTypeEnum), ...);
    Ick! Perhaps if I could send the Binders.Add method a predicate<System.Type> then I could just "myType => myType.IsEnum;" But we can't.
    Google pointed me to a post by Chuck Kinnan on the forums where he created a model binder that inherited from the default binder, overrode the protected GetPropertyValue() method, and handles the conversion there if the property type's IsEnum property is true. He's replacing the default binder with the inherited one. The post with code can be found here:

  19. Avatar for Brinkie
    Brinkie June 15th, 2011

    For me this 'kinda' solved posting decimals to and MVC ActionMethod using ajax.
    Only thing I changed to the binder was the actual parsing of the value, because in javascript a decimal is always represented in en-US format (31.2), so:
    actualValue = Convert.ToDecimal(valueResult.AttemptedValue, new CultureInfo("en-US"));
    instead of:
    actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
    Please note this would introduce issues when both decimals from form fields (say form input is in the nl-NL format with a decimal comma) and you also want do some ajax posting of a decimal to an MVC ActionMethod (e.g. using $.post() jQuery function) forcing a decimal dot there!

  20. Avatar for Travis Illig
    Travis Illig June 16th, 2011

    Was playing with this and ran into an issue.
    If you do something in a controller like this...
    return RedirectToAction("Success", new { Amount = decimalValue });
    ...then when the querystring gets generated, it comes out like this...
    It's always formatted in InvariantCulture. I *think* I tracked this down to the System.Web.Routing.ParsedRoute.Bind() method.
    The point is, if you use the culture-aware model binder and the culture uses the opposite decimal/thousands separator, you have a problem. 1234.56 is no more a valid format when dot is the thousands separator than 1234,56 is when comma is the separator. FormatException.
    I'm not sure how to overcome this. It's not safe to do a try/catch on it because which would you try first? Invariant or Specific culture? What if you guess wrong?

  21. Avatar for Travis Illig
    Travis Illig June 16th, 2011

    You know what? I'm dumb. Disregard that. I was having some interesting and confusing problems when trying to apply the same logic to integer parsing and... let's just chalk it up to trying to work to fast, shall we? :S

  22. Avatar for Travis Illig
    Travis Illig June 16th, 2011

    Oh, wait, no, I was right - there is a problem with the querystring. You can solve it by using valueResult.Culture instead of CultureInfo.CurrentCulture when you parse the decimal.
    valueResult.Culture will be CultureInfo.CurrentCulture for values posted by the Form (which will be from user input, ostensibly the culture-specific input) but will be CultureInfo.InvariantCulture for values present in the querystring (which get put there by that Bind() method I mentioned).

  23. Avatar for tobi
    tobi June 16th, 2011
    I’ll talk to the rest of my team about whether we should fix this in the next version of ASP.NET MVC, but for now it’s good to know there’s a workaround.

    hey phil,
    please strike through "whether". you definitely have to fix this. better yesterday than tomorrow. it is very annoying coding in a different locale that has , instead of . !
    ...and please don't let us wait for mvc 4 for this ;)
    thanks, tobi

  24. Avatar for Dean
    Dean July 13th, 2011

    Thanks Travis, I will try that.
    Can you include dates in this. The URL is generated with InvariantCulture, and this is not a good idea for dates, decimals, etc. There are perfectly good ISO standards for wire protocols.

  25. Avatar for Gabriel
    Gabriel July 18th, 2011

    Awesome! Thanks so much! This was driving me insane today trying to get that Model Binding figured out. Thanks for the assistance!

  26. Avatar for Paulo Diogo
    Paulo Diogo July 24th, 2011

    hey man, thx...

  27. Avatar for b2berry
    b2berry July 30th, 2011

    Thanks for this solution! Definitely resolved my decimal issues.
    I made a minor tweak to handle cases where I was using TryModelUpdate

    if (bindingContext.ModelState.ContainsKey(bindingContext.ModelName))
    bindingContext.ModelState[bindingContext.ModelName] = modelState;
    bindingContext.ModelState.Add(bindingContext.ModelName, modelState);

    Without this check, the DecimalModelBinder would attempt to add the modelState when it already exists. :)

  28. Avatar for Jack
    Jack September 20th, 2011

    yet more proof that MVC3 is a bad idea
    just uninstall it and write HttpHandlers instead - then you can be productive again

  29. Avatar for Thiago Lang
    Thiago Lang November 29th, 2011

    Great post! But still, I don't know if I'm the only one that is having some problem with this but I just wanted for the Decimal model binder to accept both Comma and Dot as a decimal separator. Depending only of the user's input. If the User inputs a decimal value like this "1.1" or like this "1,1" the app would understand both standards. How can I do that?
    Thanks in advance.

  30. Avatar for gee
    gee December 9th, 2011

    Doing a project in MVC and hating every minute of it. Longing to get back to Silverlight where you dont have to put up with all these hacks to get something to work. MVC 3 sucks big time. The time you save with view/controller generation is used up writing extension methods to cover up bugs in the ModelBinding, not to mention the miriad of bugs when dealing with decimals. Why deal with Json Ajax and jQuery when this is all taken care of for you in Silverlight. Doesnt make sense. Its a big step backwards.Of course, with IE10 not supporting plugins, its back to the stone age of javascript.

  31. Avatar for Nicolas Boisvert
    Nicolas Boisvert January 6th, 2012

    Used it and worked like a charm ! thanks a lot !

  32. Avatar for David Compton
    David Compton March 19th, 2012

    Used it and it worked beautifully - thanks Phil.

  33. Avatar for Adriano
    Adriano May 30th, 2012

    Obrigado! Ajudou bastante!

  34. Avatar for Yair Nevet
    Yair Nevet July 1st, 2012

    As a client-side complementary set you can use:

    Creating a Currency Masked TextBox with On-the-Fly Currency Formatting

  35. Avatar for Neto
    Neto September 5th, 2012

    very very nice!!! ;-) Fue muy útil esta solución.

  36. Avatar for thiago
    thiago September 10th, 2012

    This was very helpful, but damm... how come I missed your talk here, can't believe it, so sad =[

  37. Avatar for TunAntun
    TunAntun November 6th, 2012

    Can you please tell me why does the Binder always get underscore, I can't compile my project although I did all as you said. Thank you

  38. Avatar for Tripping
    Tripping November 30th, 2012

    People in India write 500,000 as 5,00,000. How should I allow model binding for this?

  39. Avatar for frankvaneykelen
    frankvaneykelen March 5th, 2013

    Please note that when your decimal property is nullable the following will not work:

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

    I had to write my own DecimalNullableModelBinder that allows null/empty values, and register it like this:

    ModelBinders.Binders.Add(typeof(decimal?), new DecimalNullableModelBinder());

    Please note difference: typeof(decimal) vs typeof(decimal?). 

    My ModelBinder has these extra lines before the try/catch:

    // Null is valid for a Nullable Decimal
    if (string.IsNullOrEmpty(valueResult.AttemptedValue))
    return actualValue;

  40. Avatar for Paul
    Paul March 18th, 2013

    Hi @Phil, you mention this works with complex objects but its not for me. The break point I set on the custom binder is only hit when I have a decimal as a route value directly e.g. RandomController(decimal someNumber) and not inside a complex binding RandomController(ComplexObj obj). Can anyone help?

  41. Avatar for Luciano
    Luciano February 6th, 2014
  42. Avatar for luciano
    luciano February 6th, 2014

    Excelent solution.

  43. Avatar for Anand
    Anand February 12th, 2014

    Is this fixed or not, because same issue occurs in MVC5 too.
    Replication procedure:
    url: '/Home/Index'
    data: SomeData',
    dataType: 'html'

    Note: Double value not bound to my object only if ajax request type is post

  44. Avatar for Tom
    Tom March 14th, 2014

    Awesome! It solved my problem.My app couldn't send decimal value with comma as fraction delimiter.

  45. Avatar for weslley39
    weslley39 June 27th, 2014

    Really thanks man, this code help me a lot

  46. Avatar for Louise
    Louise December 9th, 2014

    I had the same issue but was able to resolve it by altering DecimalModelBinder() to conditionally return a nullable decimal. In my registration I have two seperate calls to
    DecimalModelBinder(). Eg
    ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder()); and
    ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());

  47. Avatar for BOUARE
    BOUARE January 19th, 2015

    Thanks, teacher, your post help me

  48. Avatar for Peter Kula
    Peter Kula March 13th, 2015

    Has your team decided on how to handle this. Currently, still today when I use EditorFor with the currency symbol in the text it fails to bind... luckily to find this but it took me months to find this, while I did some hacky HTML stuff.

  49. Avatar for Peter Kula
    Peter Kula March 13th, 2015

    This still fails with the currency symbol so I tweaked it like this.

    decimal outValue;
    decimal.TryParse(valueResult.AttemptedValue, NumberStyles.Currency, CultureInfo.CurrentCulture, out outValue);
    actualValue = outValue;

    and that works with the current culture currency symbol, and commas and dots even in the incorrect place. Now, in my next project I am going to forget how I did this :(

  50. Avatar for Christ khodabakhshi
    Christ khodabakhshi March 28th, 2015

    Thank you, It solved my problem. Btw I am working on ASP MVC 5 and the problem still exist.

  51. Avatar for Leonardo
    Leonardo June 1st, 2015

    tks for your post! helpful and easy!

  52. Avatar for Duncan Wanjohi
    Duncan Wanjohi September 2nd, 2015

    Awesome \o/ ..... this sorted me out.

  53. Avatar for Brandon
    Brandon January 26th, 2016

    Could someone point me to an implementation of what this model binder may look like in ASP.NET 5/MVC 6? I've having trouble trying to migrate it over.

  54. Avatar for Gökmen Bulut
    Gökmen Bulut February 5th, 2016

    Thank you very much, this solved my problem :)

  55. Avatar for Eduardo
    Eduardo February 29th, 2016

    It only works for me when i remove the Currency Symbol from the string. Like this:

  56. Avatar for Mizu
    Mizu April 1st, 2016

    It's a great solution, thanks!

  57. Avatar for Nelson Reis
    Nelson Reis July 25th, 2016

    That is spot on! I was having a very strange issue and all became solved by using valueResult.Culture instead of CultureInfo.CurrentCulture! Great find.

  58. Avatar for EsseyG
    EsseyG December 8th, 2016

    It still solves my problem in ASP MVC 5. Thanks very much for the concise article.

  59. Avatar for Manuel Lombardi T.
    Manuel Lombardi T. December 14th, 2016

    Hi Phil,
    Regarding this note:

    In general, we recommend folks don’t write custom model binders because they’re difficult to get right and they’re rarely needed. The issue I’m discussing in this post might be one of those cases where it’s warranted.

    Have you changed your mind about this one? or do you still think in the same way after 5 years of software evolution?

    Please, I'm being constructive, don't get me wrong, I think now days this is changing, right? if so, maybe it's time to put a note behind,



  60. Avatar for haacked
    haacked January 2nd, 2017

    I think the advice still stands. In general, most people would write custom value providers, not model binders for their scenarios.

  61. Avatar for Manuel Lombardi T.
    Manuel Lombardi T. January 10th, 2017

    Nice, sorry for my lack of knowledge in mode binding, thank you

  62. Avatar for Zalla
    Zalla February 13th, 2017

    Today, 2017, MVC 5 and the problem is still there, but this solution still working, thanks

  63. Avatar for Raj Kiran Singh
    Raj Kiran Singh June 9th, 2017

    This wont work if there are multiple form fields with same name. Like in a grid UI with input fields.

  64. Avatar for Rafael Martins
    Rafael Martins February 5th, 2018

    O Mesmo aqui.

  65. Avatar for twyg
    twyg March 6th, 2018

    My custom model binder is executed, but the default model binder always applies afterwards. Is there anything I am missing? Do I have to explicitly exclude the default binder somehow?

  66. Avatar for Andrej
    Andrej July 19th, 2018

    It seems not to work when your model has another class with decimal property. Default model binder is going to try bind this property.

  67. Avatar for Alessandro Lettieri
    Alessandro Lettieri August 9th, 2018

    this solution don’t work for italian culture because the separator is , you must use en-GB culture

  68. Avatar for Alessandro Lettieri
    Alessandro Lettieri August 9th, 2018

    You must bind also decimal nullable:

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

  69. Avatar for Jim
    Jim October 8th, 2018

    Thanks for this, it saved me!