2010-07-02 11 views
6

Nel mio script Jquery inserisco due doppi utilizzando CultureInfo (en-UK) del browser che utilizza lo . come separatore di frazioni. La mia app MVC è in esecuzione su un server con la locale nl-BE che utilizza lo , come separatore di frazione.Problema CultureInfo con Modelbinding double in 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); 
} 

Il modelbinding non riesce a causa del problema di analisi.

Penso che sarebbe meglio avere il mio javascript impostato su en-UK e lo stesso per il modelbinding nella mia app MVC. Ma non so come fare neanche.
Qualche suggerimento?

risposta

8

Non sono sicuro della localizzazione remota con il modello predefinito (DefaultModelBinder), ma puoi creare facilmente un raccoglitore che possa gestire l'analisi specifica della lingua dei dati, ad esempio creare una nuova classe, chiamiamola è il DoubleModelBinder, copypasta il seguente:

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

Ora, quello che stiamo facendo qui, è stabilire la nostra lingua-aware doppio parser. Quando implementiamo l'interfaccia di IModelBinder, dobbiamo creare un metodo BindModel. Questo è dove la carne di esso è fatta, ma prima di poter analizzare qualcosa, abbiamo bisogno di ottenere un IFormatProvider basato sulla lingua del browser. Quindi, usiamo il metodo GetUserCulture per provare e preparare la lingua del browser. Se non possiamo tornare alla cultura attuale.

Quando abbiamo questo, siamo quindi in grado di analizzare il valore. Per prima cosa lo prendiamo dal ValueProvider (che in realtà è un insieme di molti value provider, ad esempio dalla raccolta Form, dalla raccolta di richieste, ecc.) E quindi lo analizziamo usando il IFormatProvider scoperto, che è il CultureInfo che abbiamo ora.

Una volta fatto, è piuttosto semplice aggiungerlo alla raccolta del raccoglitore di modelli;

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

Provare e vedere se questo aiuta.

+0

Che ha funzionato perfettamente. Ma ora mi chiedo come sia fatto in quello predefinito e perché è diverso in qualche modo? –

+0

Molto probabilmente perché DefaultModelBinder è un raccoglitore best-fit-for-all, non penso che sia stato davvero progettato per fare qualcosa di più complesso, quindi legare valori semplici ai modelli. –

+0

Non definirei davvero il doppio, ma vedo il tuo punto. Ad ogni modo sembra risolto e spero che non venga e mordimi più tardi. Thx :) –

1

ModelBinding utilizza CurrentCulture per analizzare i valori. È comprensibile, perché l'utente potrebbe inserire una data o un decimale in una casella di testo e il valore verrà analizzato correttamente.

Ma penso ancora che la maggior parte degli sviluppatori lo vedano nel modo in cui lo vedete: vogliono che tutti i valori vengano analizzati utilizzando la stessa cultura indipendentemente dalla lingua utilizzata dall'utente. Vogliono visualizzare i valori nel formato dell'utente ma inserire valori in un formato neutro (InvariantCulture).

Ecco perché ho impostato CurrentCulture in Application.BeginRequest su CultureInfo.InvariantCulture. Da tutto ciò Binding usa la cultura invariante. Se in un secondo momento si desidera utilizzare risorse o formattare i valori nella lingua del browser, è necessario tornare alla lingua dell'utente, impostando CurrentCulture nella lingua dell'utente. Lo faccio in un filtro azione.

EDIT:

L'OP mi ha corretto in quanto solo l'invio di moduli sono consapevoli della cultura: questo è vero. Vedere la fonte per ValueProviderDictionary: PopulateDictionary dove è documentato:

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

Le richieste sono in InvariantCulture per impostazione predefinita. Sono solo le richieste POST che sono consapevoli della cultura. Ma in qualche modo non funziona con il doppio .. Ho provato la tua idea, ma per qualche ragione sto cercando di capire adesso, BeginRequest non viene mai attivato su chiamate Ajax –

+0

Ho modificato il mio post. Le mie chiamate Ajax passano attraverso BeginRequest. Non ho idea del motivo per cui non viene chiamato nella tua applicazione. Caching? Chiama un dominio diverso, quindi pensi di farlo? –