ASP.NET MVC CurrencyBinder

Recently I have been working on an MVC 4 web portal. I had been bought on-board to help complete a live site. A problem was identified where a customer form would allow the user to enter a currency value, but the value would not persist.

The to test the binding I created a view model something like this

namespace TestWebApp.Models
{
	public class TestViewModel
	{
		[StringLength(30, MinimumLength = 3, ErrorMessage = "Invalid")]
		[Required(ErrorMessage = "Required")]
		public string StringData { get; set; }

		[Range(1, 9999, ErrorMessage = "Invalid value")]
		public int IntData { get; set; }

		[Required(ErrorMessage = "Required")]
		public decimal DecimalData { get; set; }

		[DataType(DataType.Currency)]
		[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:C2}")]
		[Required(ErrorMessage= "Required")]
		public decimal CurrencyData { get; set; }
	}
}

Usually MVC will infer the correct binding provider for each view model property and perform the correct validation for each data member. If, as in this case you are using something like jQuery client side validation, the user is provided with a really nice culture dependent currency formatted value

e.g.

£1,234.56 (en-GB)

$1,234.56 (en-US)

1 234,56 € (fr)

The problem is that the databinding tries to validate a numeric value, and throws an "invalid value" exception, this is beacuse the currency symbol is not recognised. The way to resolve this is to add a "custom" data binder for the currency data type.

#region Using
using System.Globalization;
using System.Web.Mvc;
#endregion
namespace TestWebApp.Helpers
{
    public class CurrencyBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            decimal value;
            var valueProvider = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (string.IsNullOrEmpty(valueProvider?.AttemptedValue))
                return null;
            if (bindingContext.ModelMetadata.DataTypeName == "Currency")
            {
                if (decimal.TryParse(valueProvider.AttemptedValue, NumberStyles.Currency, CultureInfo.CurrentCulture, out value))
                    return value;
            }
            else
            {
                if (decimal.TryParse(valueProvider.AttemptedValue, out value))
                    return value;
            }
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Invalid Currency");
            return null;
        }
    }
}
You will need to tell your application to use your custom binding in the application start-up.
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
            ModelBinders.Binders.Add(typeof(decimal), new CurrencyBinder());
        }

Add comment