Default ASP.NET MVC 3 model binder doesn't bind decimal properties
Asked Answered
B

2

47

For some reason, when I send this JSON to an action:

{"BaseLoanAmount": 5000}

which is supposed to be bound to a model with a decimal property named "BaseLoanAmount", it doesn't bind, it just stays 0. But if I send:

{"BaseLoanAmount": 5000.00}

it does bind the property, but why? Can't 5000 be converted to a decimal event if it doesn't have decimal numbers?

Brewer answered 18/4, 2011 at 5:45 Comment(0)
B
75

After stepping into asp.net mvc's source code, it seemsd the problem is that for the conversion asp.net mvc uses the framework's type converter, which for some reason returns false for an int to decimal conversion, I ended up using a custom model binder provider and model binder for decimals, you can see it here:

public class DecimalModelBinder : DefaultModelBinder
{
    #region Implementation of IModelBinder

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

        if (valueProviderResult.AttemptedValue.Equals("N.aN") ||
            valueProviderResult.AttemptedValue.Equals("NaN") ||
            valueProviderResult.AttemptedValue.Equals("Infini.ty") ||
            valueProviderResult.AttemptedValue.Equals("Infinity") ||
            string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
            return 0m;

       return Convert.ToDecimal(valueProviderResult.AttemptedValue);
    }    

    #endregion
}

To register this ModelBinder, just put the following line inside Application_Start():

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
Brewer answered 22/4, 2011 at 20:32 Comment(4)
Boolean expression in return statement valueProviderResult==null cannot evaluate to true, since valueProviderResult.AttemptedValue before that would throw a null-ref exception.Oilskin
note: Phil Haack also recommends this, haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspxKreitman
I've been using your original solution for exactly this problem - thanks! One issue: nulls destined for decimal? fields are assigned zero instead. I finally went with checking valueProviderResult.RawValue == null to determine whether to assign the type's default value.Engracia
Approach worked fine for me, however the code seems to be a bit inconsistent. valueProviderResult should be checked for null first to avoid NullReferenceException, currently base binding will never occur. Then, string.IsNullOrEmpty(valueProviderResult.AttemptedValue) should be the first condition to check as it again will cause NullReferenceException in current implementation. Also, it would be nice to check for -Infinity value.Eufemiaeugen
I
13

Try sending it like this:

{ "BaseLoanAmount": "5000" }
Inartistic answered 18/4, 2011 at 5:51 Comment(6)
I use JSON.stringify to prepare the JSON, is there a server side solution for this? I dont want to have to mess with the client code since it's ASP.NET MVC who is not doing its job right.Brewer
@ryudice, I am afraid that's how the javascript serializer deals with numeric formats. Things might get even messier when you start using nullable decimals, floats, ... You will find many discrepancies. Anyways according to the JSON specification quotes should always be used so I am not quite sure who is not doing his job correctly. So you might need to convert the numeric value to a string before stringifying it on the client side.Inartistic
I just found out that if I change the property type to double it binds ok, do you know if the problem could be in the value provider or in the model binder? and do you know if there is a value provider that uses JSON.net instead of the frameworks javascript deserializer?Brewer
@ryudice, the problem is in the value provider. I don't know a provider that uses JSON.NET but it seems like an easy job to write one. You just need to ensure to remove the older one or things might get even messier :-)Inartistic
Yes, if the decimal is in string format then it's correctly parsed on the server, at least in my testing.Wasteful
i use JSON.stringify but it does not work check it out - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Point

© 2022 - 2024 — McMap. All rights reserved.