Как установить десятичные разделители в ASP.NET MVC-контроллерах?
Я работаю с NerdDinner, пытающимся научить меня ASP.NET MVC. Тем не менее, я наткнулся на проблему с глобализацией, где мой сервер представляет числа с плавающей запятой с запятой в качестве разделителя с разделителями, но карта Virtual Earth требует их с точками, что вызывает некоторые проблемы.
Я уже решил проблему с отображением JavaScript в моих представлениях, но если теперь я попытаюсь опубликовать отредактированную запись в обеденном порядке с точками в виде десятичных разделителей, контроллер не выполнит (бросание InvalidOperationException
) при обновлении модели (в методе UpdateModel()
). Я чувствую, что должен установить правильную культуру где-то в контроллере, я попробовал его в OnActionExecuting()
, но это не помогло.
Ответы
Ответ 1
Я только что просмотрел проблему в реальном проекте и, наконец, нашел рабочее решение. Правильное решение состоит в том, чтобы иметь настраиваемое связующее устройство для типа decimal
(и decimal?
, если вы их используете):
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;
// 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) == -1
&& attemptedValue.IndexOf(alternateSeperator) != -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());
Обратите внимание, что код не мой, я действительно нашел его в блоге Kristof Neirynck здесь. Я только что отредактировал несколько строк и добавляю связующее для определенного типа данных, не заменяя связующее по умолчанию.
Ответ 2
Установите это в свой web.config
<globalization uiCulture="en" culture="en-US" />
Кажется, вы используете сервер, который настроен на языке, который использует запятую вместо десятичных знаков. Вы можете настроить культуру на ту, которая использует запятую таким образом, чтобы ваше приложение было разработано, например, en-US.
Ответ 3
Можете ли вы разобрать текст, используя инвариантную культуру - извините, у меня нет кода NerdDinner для меня, но если вы проходите в десятичных запятых, разделенных точками, чем разбор должен быть ОК, если вы говорите ему использовать инвариантная культура. Например.
float i = float.Parse("0.1", CultureInfo.InvariantCulture);
Edit. Я подозреваю, что это ошибка в коде NerdDinner, кстати, в том же направлении, что и предыдущая проблема.
Ответ 4
У меня другое дело, вам может понравиться. То, что мне не нравится в принятом ответе, это не проверка других символов. Я знаю, что будет случай, когда символ валюты будет в поле, потому что мой пользователь не знает лучше. Так что да, я могу проверить 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. Результат возврата.
Работает для меня.