Comment définir les séparateurs décimaux dans ASP.NET les contrôleurs MVC?
je travaille avec le NerdDinner application essayer de m'enseigner ASP.NET MVC. Cependant, je suis tombé sur un problème avec la mondialisation, où mon serveur présente des nombres à virgule flottante avec une virgule comme séparateur décimal, mais la carte virtuelle de la Terre Les nécessite avec des points, ce qui cause quelques problèmes.
j'ai déjà résolu le problème avec la cartographie JavaScript dans mes vues , mais si j'essaie maintenant de poster un entrée de dîner éditée avec des points comme séparateurs décimaux le contrôleur échoue (lancer InvalidOperationException
) lors de la mise à jour du modèle (dans le UpdateModel()
metod). Je me sens comme je dois mettre la bonne culture quelque part dans le contrôleur aussi bien, je l'ai essayé dans OnActionExecuting()
mais cela n'a pas aidé.
4 réponses
je viens de revenir sur la question dans un projet réel et finalement trouvé une solution de travail. La solution appropriée est d'avoir un classeur de modèle personnalisé pour le type decimal
(et decimal?
si vous les utilisez):
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;
}
}
puis dans Global.asax.cs dans Application_Start():
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
notez que le code n'est pas à moi, je l'ai effectivement trouvé sur le blog de Kristof Neirynck ici . J'ai juste édité quelques lignes et j'ajoute le classeur pour un type de données spécifique, ne remplaçant pas le classeur par défaut.
mettez ceci dans votre web.config
<system.web>
<globalization uiCulture="en" culture="en-US" />
vous semblez utiliser un serveur qui est configuré avec un langage qui utilise des virgule au lieu de décimales. Vous pouvez adapter la culture à une qui utilise la virgule d'une manière que votre application est conçue, comme en-US.
pouvez - vous analyser le texte en utilisant la culture invariante-désolé, je n'ai pas le code NerdDinner en fornt de moi, mais si vous passez en décimales séparées par des points que l'analyse devrait être OK si vous lui dites d'utiliser la culture invariante. Par exemple:
float i = float.Parse("0.1", CultureInfo.InvariantCulture);
Modifier . Je soupçonne que c'est un bug dans le code de NerdDinner au fait, dans le même sens que votre précédent problème.
j'ai un autre point de vue là-dessus, ça pourrait vous plaire. Ce que je n'aime pas dans la réponse acceptée, c'est qu'elle ne vérifie pas les autres caractères. Je sais qu'il y aura un cas où le symbole de devise sera dans la boîte parce que mon utilisateur ne sait pas mieux. Donc oui je peux vérifier en javascript pour l'enlever, mais si pour une raison quelconque javascript n'est-ce pas? Alors des personnages supplémentaires pourraient passer. Ou si quelqu'un essaie de spam vous passant des caractères inconnus à travers... qui sait! J'ai donc décidé de l'utilisation d'une expression régulière. Il est un peu plus lent, minuscule fraction plus lent - pour mon cas, il était de 1.000.000 itérations de la regex a pris un peu moins de 3 secondes, tandis qu'environ 1 seconde pour faire une chaîne de remplacement sur un coma et une période. Mais vu que je ne sais pas ce que les personnages pourraient traverser, alors je suis heureux pour ce moindre succès de performance.
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;
}
}
essentiellement, supprimer tous les caractères qui ne sont pas des chiffres, pour une chaîne de caractères qui n'est pas vide. Convertir en décimal. Diviser par 100. Résultat de retour.
ça me va.