Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set decimal separators in ASP.NET MVC controllers?

Tags:

I'm working with the NerdDinner application trying to teach myself ASP.NET MVC. However, I have stumbled upon a problem with globalization, where my server presents floating point numbers with a comma as the decimal separator, but Virtual Earth map requires them with dots, which causes some problems.

I have already solved the issue with the mapping JavaScript in my views, but if I now try to post an edited dinner entry with dots as decimal separators the controller fails (throwing InvalidOperationException) when updating the model (in the UpdateModel() metod). I feel like I must set the proper culture somewhere in the controller as well, I tried it in OnActionExecuting() but that didn't help.

like image 786
Pawel Krakowiak Avatar asked Apr 27 '09 13:04

Pawel Krakowiak


1 Answers

I have just revisited the issue in a real project and finally found a working solution. Proper solution is to have a custom model binder for the type decimal (and decimal? if you're using them):

using System.Globalization; using System.Web.Mvc;  public class DecimalModelBinder : DefaultModelBinder {     public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)     {         object result = null;          // Don't do this here!         // It might do bindingContext.ModelState.AddModelError         // and there is no RemoveModelError!         //          // result = base.BindModel(controllerContext, bindingContext);          string modelName = bindingContext.ModelName;         string attemptedValue = bindingContext.ValueProvider.GetValue(modelName)?.AttemptedValue;          // in decimal? binding attemptedValue can be Null         if (attemptedValue != null)         {             // Depending on CultureInfo, the NumberDecimalSeparator can be "," or "."             // Both "." and "," should be accepted, but aren't.             string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;             string alternateSeperator = (wantedSeperator == "," ? "." : ",");              if (attemptedValue.IndexOf(wantedSeperator, StringComparison.Ordinal) == -1                 && attemptedValue.IndexOf(alternateSeperator, StringComparison.Ordinal) != -1)             {                 attemptedValue = attemptedValue.Replace(alternateSeperator, wantedSeperator);             }              try             {                 if (bindingContext.ModelMetadata.IsNullableValueType && string.IsNullOrWhiteSpace(attemptedValue))                 {                     return null;                 }                  result = decimal.Parse(attemptedValue, NumberStyles.Any);             }             catch (FormatException e)             {                 bindingContext.ModelState.AddModelError(modelName, e);             }         }          return result;     } } 

Then in Global.asax.cs in Application_Start():

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

Note that code is not mine, I actually found it at Kristof Neirynck's blog here. I just edited a few lines and am adding the binder for a specific data type, not replacing the default binder.

like image 176
Pawel Krakowiak Avatar answered Sep 24 '22 20:09

Pawel Krakowiak