Как установить десятичные разделители в контроллерах ASP.NET MVC? - PullRequest
20 голосов
/ 27 апреля 2009

Я работаю с приложением NerdDinner , пытаясь научить себя ASP.NET MVC. Однако я наткнулся на проблему с глобализацией, когда мой сервер представляет числа с плавающей запятой в качестве десятичного разделителя через запятую, а карта Virtual Earth требует их с точками, что вызывает некоторые проблемы.

Я уже решил проблему с отображением JavaScript в моих представлениях , но если я сейчас попытаюсь опубликовать отредактированную запись обеденного стола с точками в качестве десятичных разделителей, то при обновлении произойдет сбой контроллера (выбрасывая InvalidOperationException) модель (в методе UpdateModel()). Я чувствую, что должен установить правильную культуру где-то в контроллере, я попробовал это в OnActionExecuting(), но это не помогло.

Ответы [ 4 ]

58 голосов
/ 25 февраля 2011

Я только что вернулся к проблеме в реальном проекте и, наконец, нашел рабочее решение. Правильное решение состоит в том, чтобы иметь пользовательское связующее для модели типа decimaldecimal?, если вы их используете):

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;
    }
}

Затем в Global.asax.cs в Application_Start ():

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

Обратите внимание, что код не мой, я действительно нашел его в блоге Кристофа Нейринка здесь . Я только что отредактировал несколько строк и добавляю подшивку для определенного типа данных, а не заменяю подшивку по умолчанию.

5 голосов
/ 27 апреля 2009

Установите это в вашем web.config

  <system.web>
    <globalization uiCulture="en" culture="en-US" />

Вы, похоже, используете сервер, настроенный на языке, в котором вместо запятой используются запятые. Вы можете настроить язык, который использует запятую, таким образом, как ваше приложение, например, en-US.

0 голосов
/ 11 ноября 2015

У меня другой взгляд на это, вам это может понравиться. Что мне не нравится в принятом ответе, так это то, что он не проверяет другие символы. Я знаю, что будет случай, когда символ валюты будет в коробке, потому что мой пользователь не знает лучше. Так что да, я могу проверить в javascript, чтобы удалить его, но что если по какой-то причине javascript не включен? Тогда дополнительные символы могут пройти. Или если кто-то пытается спамить вас, передавая неизвестных персонажей через ... кто знает! Поэтому я решил использовать регулярное выражение. Это немного медленнее, крошечная дробь медленнее - в моем случае это было 1 000 000 итераций регулярного выражения, что заняло чуть менее 3 секунд, в то время как около 1 секунды для замены строки в коме и точке Но, учитывая, что я не знаю, какие персонажи могут пройти, я рад за это малейшее количество хитов.

public class DecimalModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext,
                                     ModelBindingContext bindingContext)
    {
        string modelName = bindingContext.ModelName;
        string attemptedValue =
            bindingContext.ValueProvider.GetValue(modelName).AttemptedValue;

        if (bindingContext.ModelMetadata.IsNullableValueType
                && string.IsNullOrWhiteSpace(attemptedValue))
        {
            return null;
        }

        if (string.IsNullOrWhiteSpace(attemptedValue))
        {
            return decimal.Zero;
        }

        decimal value = decimal.Zero;
        Regex digitsOnly = new Regex(@"[^\d]", RegexOptions.Compiled);
        var numbersOnly = digitsOnly.Replace(attemptedValue, "");
        if (!string.IsNullOrWhiteSpace(numbersOnly))
        {
            var numbers = Convert.ToDecimal(numbersOnly);
            value = (numbers / 100m);

            return value;
        }
        else
        {
            if (bindingContext.ModelMetadata.IsNullableValueType)
            {
                return null;
            }

        }

        return value;
    }
}

В основном удалите все символов, которые не являются цифрами, для строки, которая не является пустой. Преобразовать в десятичную. Разделите на 100. Верните результат.

У меня работает.

0 голосов
/ 27 апреля 2009

Можете ли вы разобрать текст, используя инвариантную культуру - извините, у меня нет кода NerdDinner, но если вы передаете десятичные дроби, разделенные точкой, синтаксический анализ должен быть в порядке, если вы скажете ему использовать инвариантная культура. Э.Г.

 float i = float.Parse("0.1", CultureInfo.InvariantCulture);

Редактировать . Я подозреваю, что это ошибка в коде NerdDinner, кстати, по той же схеме, что и ваша предыдущая проблема.

...