Проблема CultureInfo с двойным связыванием моделей в asp.net-mvc (2) - PullRequest
6 голосов
/ 02 июля 2010

В моем скрипте Jquery я публикую два дубликата, используя браузер CultureInfo (en-UK), который использует . в качестве разделителя дробей. Мое приложение MVC работает на сервере с языковым стандартом nl-BE, использующим , в качестве разделителя дробей.

[AcceptVerbs(HttpVerbs.Post)]
public JsonResult GetGridCell(double longitude, double latitude)
{
    var cell = new GridCellViewModel { X = (int)Math.Round(longitude, 0), Y = (int)Math.Round(latitude, 0) };
    return Json(cell);
}

Ошибка привязки модели из-за проблемы с синтаксическим анализом.

Я думаю, что было бы лучше, чтобы мой javascript был установлен в en-UK и такой же для привязки модели в моем приложении MVC. Но я тоже не знаю, как это сделать.
Есть предложения?

Ответы [ 2 ]

8 голосов
/ 04 июля 2010

Я не уверен, насколько далеко идет локализация с помощью связывателя модели по умолчанию (DefaultModelBinder), но вы можете легко создать связыватель самостоятельно, который может обрабатывать специфический для культуры анализ данных, например, создать новый класс, давайте назовем егоDoubleModelBinder, copypasta, следующий:

public class DoubleModelBinder : IModelBinder
{
    /// <summary>
    /// Binds the value to the model.
    /// </summary>
    /// <param name="controllerContext">The current controller context.</param>
    /// <param name="bindingContext">The binding context.</param>
    /// <returns>The new model.</returns>
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var culture = GetUserCulture(controllerContext);

        string value = bindingContext.ValueProvider
                           .GetValue(bindingContext.ModelName)
                           .ConvertTo(typeof(string)) as string;

        double result = 0;
        double.TryParse(value, NumberStyles.Any, culture, out result);

        return result;
    }

    /// <summary>
    /// Gets the culture used for formatting, based on the user's input language.
    /// </summary>
    /// <param name="context">The controller context.</param>
    /// <returns>An instance of <see cref="CultureInfo" />.</returns>
    public CultureInfo GetUserCulture(ControllerContext context)
    {
        var request = context.HttpContext.Request;
        if (request.UserLanguages == null || request.UserLanguages.Length == 0)
            return CultureInfo.CurrentUICulture;

        return new CultureInfo(request.UserLanguages[0]);
    }
}

Теперь, то, что мы делаем здесь, - это создание нашего собственного синтаксического анализатора с поддержкой языка.Когда мы реализуем интерфейс IModelBinder, нам нужно создать метод BindModel.Вот где это сделано, но прежде чем мы сможем что-то анализировать, нам нужно получить IFormatProvider на основе языка браузера.Итак, мы используем метод GetUserCulture, чтобы попытаться подготовить язык браузера.Если мы не можем вернуться к текущей культуре.

Когда у нас это есть, мы можем проанализировать значение.Сначала мы получаем его из ValueProvider (который на самом деле является составной частью многих поставщиков значений, например, из коллекции Form, коллекции Request и т. Д.), А затем анализируем его, используя обнаруженный IFormatProvider, который представляет собой CultureInfo, который у нас теперь есть.

Как только вы это сделаете, добавить тривиум в коллекцию связующих моделей довольно просто;

ModelBinder.Binders[typeof(Double)] = new DoubleModelBinder();

Попробуйте и посмотрите, поможет ли это.

1 голос
/ 04 июля 2010

ModelBinding использует CurrentCulture для анализа значений.Это понятно, потому что пользователь может ввести дату или десятичное число в текстовое поле, и значение будет проанализировано правильно.

Но я все же думаю, что большинство разработчиков видят это так, как вы это видите: они хотят, чтобы все значения были проанализированыиспользуя ту же культуру, независимо от того, какой язык использует пользователь.Они хотят отображать значения в формате пользователя, но вводят значения в нейтральном формате (InvariantCulture).

Вот почему я установил CurrentCulture в Application.BeginRequest в CultureInfo.InvariantCulture.При этом все связывание использует инвариантную культуру.Если позже вы захотите использовать Ressources или отформатировать значения на языке браузера, вам придется переключиться обратно на язык пользователя, снова установив CurrentCulture на язык пользователя.Я делаю это в фильтре действий.

РЕДАКТИРОВАТЬ:

ОП исправил меня в том, что только представления формы осведомлены о культуре: это правда.См. Источник для ValueProviderDictionary: PopulateDictionary, где это задокументировано:

   We use this order of precedence to populate the dictionary:
   1. Request form submission (should be culture-aware)
   2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
   3. URI query string
...