2009-04-27 9 views
18

Pracuję z aplikacją NerdDinner próbując nauczyć się ASP.NET MVC. Jednak natknąłem się na problem z globalizacją, gdzie mój serwer przedstawia liczby zmiennoprzecinkowe z przecinkiem jako separatorem dziesiętnym, ale mapa Virtual Earth wymaga ich kropek, co powoduje pewne problemy.Jak ustawić separatory dziesiętne w kontrolerach ASP.NET MVC?

już rozwiązany the issue with the mapping JavaScript in my views, ale jeśli ja teraz staram się pisać edytowany wpis kolację z kropek jako separatorów dziesiętnych kontroler nie powiedzie się (rzucanie InvalidOperationException) podczas aktualizacji modelu (w UpdateModel() Metodego). Czuję, że muszę ustawić właściwą kulturę gdzieś w kontrolerze, próbowałem tego w OnActionExecuting(), ale to nie pomogło.

Odpowiedz

54

Po prostu ponownie przeanalizowałem problem w prawdziwym projekcie i ostatecznie znalazłem działające rozwiązanie. Prawidłowe rozwiązanie ma mieć spinacza modelu niestandardowych dla typu decimal (i decimal? jeśli ich użyciem):

using System.Globalization; 
using System.Web.Mvc; 

public class DecimalModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, 
            ModelBindingContext bindingContext) 
    { 
     object result = null; 

     // Don't do this here! 
     // It might do bindingContext.ModelState.AddModelError 
     // and there is no RemoveModelError! 
     // 
     // result = base.BindModel(controllerContext, bindingContext); 

     string modelName = bindingContext.ModelName; 
     string attemptedValue = 
      bindingContext.ValueProvider.GetValue(modelName).AttemptedValue; 

     // Depending on CultureInfo, the NumberDecimalSeparator can be "," or "." 
     // Both "." and "," should be accepted, but aren't. 
     string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator; 
     string alternateSeperator = (wantedSeperator == "," ? "." : ","); 

     if (attemptedValue.IndexOf(wantedSeperator) == -1 
      && attemptedValue.IndexOf(alternateSeperator) != -1) 
     { 
      attemptedValue = 
       attemptedValue.Replace(alternateSeperator, wantedSeperator); 
     } 

     try 
     { 
      if (bindingContext.ModelMetadata.IsNullableValueType 
       && string.IsNullOrWhiteSpace(attemptedValue)) 
      { 
       return null; 
      } 

      result = decimal.Parse(attemptedValue, NumberStyles.Any); 
     } 
     catch (FormatException e) 
     { 
      bindingContext.ModelState.AddModelError(modelName, e); 
     } 

     return result; 
    } 
} 

Następnie w Global.asax.cs w Application_Start():

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder()); 
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder()); 

Zauważ, że kod nie jest mój, faktycznie znalazłem go na blogu Kristof Neirynck here. Właśnie edytowałem kilka linii i dodawałem segregator dla określonego typu danych, nie zastępując domyślnego segregatora.

+2

Właśnie zaktualizowałem swoją odpowiedź, dodając dodatkowe sprawdzenie kodu, aby poprawnie obsługiwać właściwości zerowania. Odkryłem, że jeśli znajdzie pusty ciąg jako wartość próby, wyrzuci wyjątek, nawet jeśli powiązana właściwość jest zerowa. Teraz powinno działać dobrze. –

+1

Istnieje dodatkowa kompilacja po stronie klienta, która powinna zostać zastosowana wzdłuż tej. Znajdziesz go tutaj: http://stackoverflow.com/a/8102159/41420 –

+0

Nie działa z ciągiem znaków jako danych wejściowych – Furtiro

4

Zestaw ten w web.config

<globalization uiCulture="en" culture="en-US" /> 

pojawiasz się przy użyciu serwera, który jest instalowany w języku, który używa przecinek zamiast miejsc po przecinku. Możesz dostosować kulturę do takiej, która używa przecinka w sposób, w jaki twoja aplikacja jest zaprojektowana, na przykład en-US.

+1

Już to mam, z jakiegoś powodu nie działa. –

+0

co z nagłówkami na stronie aspx? Czy któryś z nich ma coś dla kultury? –

+0

@Nick: Nie, nie ma tam żadnych niestandardowych rzeczy. –

0

Czy możesz przeanalizować tekst za pomocą kultury niezmiennej - przepraszam, nie mam kodu NerdDinner w moim przypadku, ale jeśli przechodzisz w ułamkach dziesiętnych z kropkami, to parsowanie powinno być OK, jeśli powiesz to użyj kultury niezmiennej. Na przykład.

float i = float.Parse("0.1", CultureInfo.InvariantCulture); 

Edit. Podejrzewam, że jest to błąd w kodzie NerdDinner, przy okazji, podobnie jak poprzedni problem.

+0

Nie, nie dotykam żadnych właściwości. Ma się "magicznie" zdarzyć w UpdateModel(), ale rzuca niezbyt pomocny wyjątek bez szczegółów. Występują dwa problemy - podczas prezentacji danych z modelu potrzebuję kropek, ponieważ odwzorowanie mapujące JS zostanie przerwane, jednak przy wprowadzaniu danych z powrotem do modelu potrzebuję przecinków tym razem, bo inaczej kontroler się zepsuje. To wszystko jest trochę dziwne i nie jestem pewien, jak rozwiązać ten problem, Google nie pomogło, tak samo jak SO. :( –

+0

Tak, nie sądzę, że możesz go rozwiązać bez zmiany kodu. Mam kod ND, a ja zajrzę kiedyś i spróbuję to zweryfikować i wysłać ci poprawkę, jeśli mogę, ale jestem Obawiam się, że nie będzie to już tak szybko, jak trzeba pracować :-) –

+0

OK, myślę, że trzeba cofnąć zmiany dokonane w drugim pytaniu, wysłałem sugestię, jak rozwiązać ten problem i mam nadzieję, że moja poprawka nie powoduje efektu ubocznego, który tu widzicie. –

0

Mam inne podejście do tego, może ci się spodobać. To, co mi się nie podoba w zaakceptowanej odpowiedzi, to nie sprawdza innych postaci. Wiem, że będzie przypadek, w którym symbol waluty będzie w pudełku, ponieważ mój użytkownik nie wie lepiej. Tak więc mogę sprawdzić javascript, aby go usunąć, ale co jeśli z jakiegoś powodu javascript nie jest włączony? Wtedy dodatkowe postacie mogą się przedostać. Lub jeśli ktoś próbuje spammować Cię przez nieznane postacie przez ... kto wie! Postanowiłem więc użyć wyrażenia regularnego. Trochę wolniej, maleńki ułamek wolniej - w moim przypadku było to 1 000 000 powtórzeń regexu trwało nieco mniej niż 3 sekundy, a około 1 sekundy, aby zastąpić ciąg w stanie śpiączki i kropki. Ale widząc, że nie wiem, przez co mogą przechodzić postaci, jestem szczęśliwy z powodu najdrobniejszych hitów wydajnościowych.

public class DecimalModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, 
            ModelBindingContext bindingContext) 
    { 
     string modelName = bindingContext.ModelName; 
     string attemptedValue = 
      bindingContext.ValueProvider.GetValue(modelName).AttemptedValue; 

     if (bindingContext.ModelMetadata.IsNullableValueType 
       && string.IsNullOrWhiteSpace(attemptedValue)) 
     { 
      return null; 
     } 

     if (string.IsNullOrWhiteSpace(attemptedValue)) 
     { 
      return decimal.Zero; 
     } 

     decimal value = decimal.Zero; 
     Regex digitsOnly = new Regex(@"[^\d]", RegexOptions.Compiled); 
     var numbersOnly = digitsOnly.Replace(attemptedValue, ""); 
     if (!string.IsNullOrWhiteSpace(numbersOnly)) 
     { 
      var numbers = Convert.ToDecimal(numbersOnly); 
      value = (numbers/100m); 

      return value; 
     } 
     else 
     { 
      if (bindingContext.ModelMetadata.IsNullableValueType) 
      { 
       return null; 
      } 

     } 

     return value; 
    } 
} 

Zasadniczo usunąć wszystkie znaki, które nie są cyfry, na sznurku, który nie jest pusty. Konwertuj na liczbę dziesiętną. Podziel przez 100. Wynik zwrotu.

Działa dla mnie.

+1

Chociaż działa to dobrze w niektórych przypadkach, jeśli dziesiętnie nie ma wartości .00 (tj. Istnieje walidacja javascript lub coś innego, co je usuwa), technika dzielenia przez 100 nie działa. – Ben

+0

Tak. Zauważyłem, że w kilku okolicznościach, a następnie upewnij się, że mój kod zawsze dodaje miejsca dziesiętne za pomocą JavaScript przed wysłaniem. Ale w prawdziwym życiu nie można zawsze oczekiwać, że tak się stanie. –

Powiązane problemy