Проблема CultureInfo с двойным связыванием модели в asp.net-mvc (2)

В моем сценарии 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. Но я тоже не знаю, как это сделать.
Есть предложения?


person Boris Callens    schedule 02.07.2010    source источник


Ответы (2)


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

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();

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

person Matthew Abbott    schedule 04.07.2010
comment
Это сработало отлично. Но теперь мне интересно, как это делается в стандартном и почему он каким-то образом отличается? - person Boris Callens; 05.07.2010
comment
Скорее всего, поскольку DefaultModelBinder является наиболее подходящим для всех связыванием, я не думаю, что он действительно был разработан для выполнения чего-либо более сложного, чем привязка простых значений к моделям. - person Matthew Abbott; 05.07.2010
comment
Я бы не назвал парный разряд сложным, но я понимаю вашу точку зрения. В любом случае, похоже, это решено, и я надеюсь, что позже он не придет и не укусит меня в спину. Спасибо :) - person Boris Callens; 05.07.2010

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

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

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

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

OP поправил меня, сказав, что только отправка форм учитывает культуру: это правда. См. Источник 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
person Mathias F    schedule 04.07.2010
comment
По умолчанию запросы на получение находятся в InvariantCulture. Только запросы POST учитывают культуру. Но почему-то он не работает с дублями .. Я пробовал вашу идею, но по какой-то причине сейчас пытаюсь понять, BeginRequest никогда не запускается при вызовах ajax - person Boris Callens; 05.07.2010
comment
Я отредактировал свой пост. Мои вызовы Ajax проходят через BeginRequest. Понятия не имею, почему он не вызывается в вашем приложении. Кеширование? Позвонить в другой домен, чем вы думаете? - person Mathias F; 05.07.2010