2010-07-02 12 views
6

En mi script de Jquery, publico dos dobles utilizando el CultureInfo (en-UK) del navegador que usa el . como separador de fracciones. Mi aplicación MVC se ejecuta en un servidor con local nl-BE utilizando el , como separador de fracciones.Problema CultureInfo con Modelbinding double en asp.net-mvc (2)

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

El enlace de modelo falla debido a la cuestión del análisis sintáctico.

Creo que sería mejor tener mi javascript configurado en Reino Unido y lo mismo para el enlace de modelo en mi aplicación MVC. Pero no sé cómo hacer ninguna de las dos cosas.
¿Alguna sugerencia?

Respuesta

8

No estoy seguro de cuán lejos va la localización con el archivador de modelos predeterminado (DefaultModelBinder), pero puede crear fácilmente un archivador que pueda manejar el análisis cultural específico de los datos, por ejemplo, crear una nueva clase, llamemos que la DoubleModelBinder, copypasta lo siguiente:

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

Ahora, lo que estamos haciendo aquí, es el establecimiento de nuestro propio lenguaje-conscientes doble analizador. Cuando implementamos la interfaz IModelBinder, necesitamos crear un método BindModel. Aquí es donde se hace la carne, pero antes de que podamos analizar algo, necesitamos obtener un proveedor de archivos basado en el idioma del navegador. Entonces, usamos el método GetUserCulture para tratar de preparar el idioma del navegador. Si no podemos volver a la cultura actual.

Cuando tenemos eso, estamos en condiciones de analizar el valor. Primero lo tomamos del ValueProvider (que en realidad es un compuesto de muchos proveedores de valor, por ejemplo, de la colección de Formularios, de la colección de Solicitudes, etc.), y luego lo analizamos utilizando el IFormatProvider descubierto, que es el CultureInfo que ahora tenemos.

Una vez que haya hecho eso, es bastante trivial agregarlo a la colección de carpetas de modelo;

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

Pruébalo y ver si eso ayuda.

+0

Eso funcionó perfectamente. Pero ahora me pregunto cómo se hace esto en el modo predeterminado y por qué es diferente de alguna manera. –

+0

Probablemente porque DefaultModelBinder es el archivador que mejor se adapta a todos, no creo que realmente esté diseñado para hacer algo más complejo que enlazar valores simples a los modelos. –

+0

No llamaría complejo de dobles realmente, pero veo tu punto. De todos modos parece resuelto y espero que no venga y me muerda en la espalda más tarde. Thx :) –

1

ModelBinding utiliza CurrentCulture para analizar valores. Eso es comprensible, porque el usuario puede ingresar una fecha o un decimal en un cuadro de texto y el valor se analizará correctamente.

Pero sigo pensando que la mayoría de los desarrolladores lo ven de la manera que lo ven: quieren que todos los valores se analicen utilizando la misma cultura, sin importar el idioma que use el usuario. Quieren mostrar valores en el formato del usuario pero ingresar valores en un formato neutral (InvariantCulture).

Es por eso que establecí el CurrentCulture en Application.BeginRequest en CultureInfo.InvariantCulture. Por eso, todos los enlaces utilizan la cultura invariante. Si luego desea utilizar Ressources o valores de formato en el idioma del navegador, debe volver al idioma del usuario, configurando CurrentCulture en el idioma del usuario nuevamente. Lo hago en un filtro de acción.

EDIT:

El PO me corrigió en el que sólo los envíos de formularios son conscientes de la cultura: eso es cierto. Consulte la fuente de ValueProviderDictionary: PopulateDictionary donde está documentado:

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 
+0

Las solicitudes de obtención están en InvariantCulture por defecto. Solo las solicitudes POST son conscientes de la cultura. Pero de alguna manera no funciona con los dobles ... He intentado tu idea, pero por alguna razón estoy tratando de averiguar ahora, el BeginRequest nunca se desencadena en las llamadas ajax –

+0

Edité mi publicación. Mis llamadas Ajax pasan por BeginRequest. No tengo idea de por qué no se llama en su aplicación. ¿Caché? Llamar a un dominio diferente, entonces crees que haces? –