Poprawna walidacja wartości dziesiętnych dla przecinka („,”) i kropki („.”) równocześnie.

Piszesz aplikacje (ASP MVC) która ma pola finansowe. Na pytanie jak w jaki sposób chcecie oddzielać część dziesiętną, otrzymujesz odpowiedz że przy pomocy przecinka oraz kropki. Odpowiedz budzi pewien niepokój, ponieważ standardowy mechanizm wiązania sobie nie poradzi. Wiec trzeba go nieco ulepszyć.
DecimalModelBinder:

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 == null) 
        {
            return base.BindModel(controllerContext, bindingContext);
        }

        ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        ModelState modelState = new ModelState { Value = valueResult };
        object actualValue = null;

        if (string.IsNullOrEmpty(valueProviderResult.AttemptedValue) ||
            valueProviderResult.AttemptedValue.Equals("N.aN") ||
            valueProviderResult.AttemptedValue.Equals("NaN") ||
            valueProviderResult.AttemptedValue.Equals("Infini.ty") ||
            valueProviderResult.AttemptedValue.Equals("Infinity"))
        {
            actualValue = 0m;
        }
        else
        {
            try
            {
                actualValue = Convert.ToDecimal(valueResult.AttemptedValue.Replace(',', '.'), CultureInfo.GetCultureInfo("en-US"));
            }
            catch (FormatException e)
            {
                modelState.Errors.Add(e);
            }
            bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
        }

        return actualValue;
    }

    #endregion
}

Wersja dla decimal?, wymaga minimalnej korekty.
NullableDecimalModelBinder:

public class NullableDecimalModelBinder : DefaultModelBinder
{
    #region Implementation of IModelBinder

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

        if(valueProviderResult == null) 
        {
            return base.BindModel(controllerContext, bindingContext);
        }

        ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        ModelState modelState = new ModelState { Value = valueResult };
        object actualValue = null;

        if (string.IsNullOrEmpty(valueProviderResult.AttemptedValue) ||
            valueProviderResult.AttemptedValue.Equals("N.aN") ||
            valueProviderResult.AttemptedValue.Equals("NaN") ||
            valueProviderResult.AttemptedValue.Equals("Infini.ty") ||
            valueProviderResult.AttemptedValue.Equals("Infinity"))
        {
            actualValue = null;
        }
        else
        {
            try
            {
                actualValue = Convert.ToDecimal(valueResult.AttemptedValue.Replace(',', '.'), CultureInfo.GetCultureInfo("en-US"));
            }
            catch (FormatException e)
            {
                modelState.Errors.Add(e);
            }
            bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
        }

        return actualValue;
    }

    #endregion
}

Logiczna wydaje się refaktoryzacja, ale to nie jest tematem tego wpisu.

Aktywacja:

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

Po użyciu powyższego kodu, serwer przyjmie zarówno wartość 123,45 jak i 123.45.

Share Button

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Time limit is exhausted. Please reload the CAPTCHA.