CultureInfo issue with Modelbinding double in asp.net-mvc(2)

笑着哭i 提交于 2019-12-03 14:11:11

I'm not sure how far localisation goes with the default model binder (DefaultModelBinder), but you can easily create a binder yourself that can handle the culture specific parsing of the data, e.g, create a new class, let's call it the DoubleModelBinder, copypasta the following:

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

Now, what we are doing here, is establishing our own language-aware double-parser. When we implement the IModelBinder interface, we need to create a BindModel method. This is where the meat of the it is done, but before we can parse anything, we need to get an IFormatProvider based on the browser's language. So, we use the GetUserCulture method to try and ready the browser's language. If we can't revert to the current culture.

When we have that, we are then in a position to parse the value. We first grab it from the ValueProvider (which is really a composite of many value providers, e.g. from the Form collection, Request collection, etc.), and then we parse it using the discovered IFormatProvider, which is the CultureInfo we now have.

Once you've done that, it's pretty trivial to add it to the model binder collection;

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

Try that and see if that helps.

ModelBinding uses CurrentCulture to parse values. Thats understandable, because the user might enter a date or decimal into a textbox and the value would be parsed correctly.

But I still think most developers see it the way you see it: They want that all values are parsed by using the same culture no matter what language the user uses. They want to display values in the format of the user but enter values in a neutral format (InvariantCulture).

Thats why I set the CurrentCulture in Application.BeginRequest to CultureInfo.InvariantCulture. By that all Binding uses the invariant Culture. If you later want to use Ressources or format values in the language of the browser you have to switch back to the language of the user, by setting CurrentCulture to the language of the user again. I do this in an action filter.

EDIT:

The OP corrected me in that only Form submissions are culture aware: thats true. See the source for ValueProviderDictionary:PopulateDictionary where it is documented:

   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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!