0

We have large MVC(5.0) application in which we were saving lots of decimal data in database. There are currency and decimal inputs from UI. Now a new change request came to show all numeric values in comma separated numbers in UI and forms. We have a jQuery function which changes these values to proper format.

Our classes fields are defined as decimal to accept these values. Problem comes when I try to save data with comma. We are using jQuery.ajax to POST forms which is encrypting comma separated numbers.

I don't want to go and modify each forms with cultureInfo. Is there any better way, where I set once and it will accept and convert these comma separated numbers when I try to save in db?

I tried setting culture info in Global.asax like:

protected void Application_BeginRequest()
{
        System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
}

This doesn't seems to be working. Is there any nice and clean way to fix this issue without changing each fields?

sarojanand
  • 607
  • 1
  • 11
  • 30

2 Answers2

0

I had the same problem and I solved my problem by writing a ModelBinder as below

public class MyModelBinder : DefaultModelBinder
{
     public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(decimal) || bindingContext.ModelType == typeof(Nullable<decimal>))
        {
            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (valueProviderResult != null)
            {
                decimal result;
                var array = valueProviderResult.RawValue as Array;
                string value;
                if (array != null && array.Length > 0)
                {
                    value = array.GetValue(0).ToString().Replace(",", ""); ;
                    if (decimal.TryParse(value.ToString(), out result))
                    {
                        string val = result.ToString(CultureInfo.InvariantCulture.NumberFormat);
                        array.SetValue(val, 0);
                    }
                }
            }
        }
        else if (bindingContext.ModelType == typeof(int) || bindingContext.ModelType == typeof(Nullable<int>))
        {
            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (valueProviderResult != null)
            {
                int result;
                var array = valueProviderResult.RawValue as Array;
                string value;
                if (array != null && array.Length > 0)
                {
                    value = array.GetValue(0).ToString().Replace(",", ""); 
                    if (int.TryParse(value.ToString(), out result))
                    {
                        string val = result.ToString(CultureInfo.InvariantCulture.NumberFormat);
                        array.SetValue(val, 0);
                    }
                }
            }
        }
        else if (bindingContext.ModelType == typeof(long) || bindingContext.ModelType == typeof(Nullable<long>))
        {
            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (valueProviderResult != null)
            {
                long result;
                var array = valueProviderResult.RawValue as Array;
                string value;
                if (array != null && array.Length > 0)
                {
                    value = array.GetValue(0).ToString().Replace(",","");
                    if (long.TryParse(value, out result))
                    {
                        string val = result.ToString(CultureInfo.InvariantCulture.NumberFormat);
                        array.SetValue(val, 0);
                    }
                }
            }
        }
        var res = base.BindModel(controllerContext, bindingContext);
        return res;
    }
}

and add these codes to Application_Start in global.asax

        ModelBinders.Binders.DefaultBinder = new MyModelBinder();
Reza
  • 18,865
  • 13
  • 88
  • 163
  • thanks but it will be called everytime for each control which looks over kill. I want to call it only for decimal types. – sarojanand Jan 26 '15 at 22:56
  • Sine there is already a `DefaultBinder` it is called everytime for each action and since there are `if` to check the type don't worry about being over kill ;) – Reza Jan 27 '15 at 04:57
0

Finally I found a solution with 3 steps:

  1. Add class to override DefaultModelBinder

    public class DecimalModelBinder : DefaultModelBinder
    {
       public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
       {
        var valueProvider = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    
        object result = null;
        try
        {
            if (!string.IsNullOrEmpty(valueProvider.AttemptedValue))
                result = decimal.Parse(valueProvider.AttemptedValue);
        }
        catch (FormatException e)
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, e);
        }
    
        return result;
    }
    

    }

  2. Call this class in Global.asax in Application_Start

    ModelBinders.Binders.Add(typeof(decimal), new GOA.Department.ANS.Web.Model.DecimalModelBinder());
    ModelBinders.Binders.Add(typeof(decimal?), new GOA.Department.ANS.Web.Model.DecimalModelBinder());
    

Above code will solve the server side validation. We still need to validate on client side using jquery.validate. The answer is to extend validate method.

  1. After jquery.validate.js is called, add these lines in your page or create a separate .js file which will be called at last:

    $.validator.methods.range = function (value, element, param) {
      var val = value.replace(",", "");
      return this.optional(element) || (val >= param[0] && val <= param[1]);
    }
    
    $.validator.methods.min = function (value, element, param) {
      var val = value.replace(",", "");
      return this.optional(element) || (val >= param);
    }
    
    $.validator.methods.max = function (value, element, param) {
      var val = value.replace(",", "");
      return this.optional(element) || (val <= param);
    }
    
    $.validator.methods.number = function (value, element) {
      return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/.test(value);
    }
    
sarojanand
  • 607
  • 1
  • 11
  • 30