2009-04-27 44 views
18

我正在与尝试自学ASP.NET MVC的NerdDinner应用程序一起工作。然而,我偶然发现了一个全球化问题,我的服务器用浮点数作为小数点分隔符,但虚拟地球地图要求它们带有点,这会导致一些问题。如何在ASP.NET MVC控制器中设置小数点分隔符?

我已经解决the issue with the mapping JavaScript in my views,但如果我现在尝试后用点的编辑晚餐条目小数点分隔符的控制器出现故障更新模型(在UpdateModel()梅托德)时(投掷InvalidOperationException)。我觉得我必须在控制器的某个地方设置适当的文化,我在OnActionExecuting()中尝试过,但这并没有帮助。

回答

54

我刚刚在一个真实的项目中重新讨论了这个问题,并最终找到了一个工作解决方案。正确的解决办法是有类型decimal(和decimal?如果你正在使用它们)的自定义模型绑定:

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

然后在Global.asax.cs中的的Application_Start():

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

请注意,代码不是我的,我在Kristof Neirynck的博客here上发现它。我只编辑了几行,并为特定数据类型添加了活页夹,而不是替换默认活页夹。

4

集这你的web.config

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

你似乎是使用服务器是安装与使用逗号,而不是小数的语言。您可以根据应用程序的设计方式将文化调整为使用逗号的文化,例如en-US。

+1

我已经有这个,由于某种原因它不起作用。 – 2009-04-28 08:28:22

+0

你的aspx页面中的标题怎么样?他们中的任何一个人是否有为文化设置的东西 – 2009-04-28 13:22:43

+0

@尼克:没有,那里没有自定义的东西。 – 2009-05-11 19:25:52

0

你可以使用不变文化解析文本 - 对不起,我没有NerdDinner代码,但如果你传递点分隔的小数而不是解析应该可以,如果你告诉它使用不变的文化。例如。

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

编辑。顺便提一下,我怀疑这是NerdDinner代码中的一个错误,与前面的问题一样。

0

我有不同的看法,你可能会喜欢它。我不喜欢接受的答案是它不检查其他角色。我知道会出现这样一种情况,货币符号会出现在框中,因为我的用户不知道的更好。所以是的,我可以检查在JavaScript中删除它,但如果由于某种原因JavaScript不在?然后额外的字符可能会通过。或者,如果有人试图发送垃圾邮件,通过未知字符......谁知道!所以我决定使用正则表达式。它有点慢,只有很小的一小部分 - 对于我来说,正则表达式的迭代次数为1,000,000次,时间不到3秒,而在昏迷和一段时间内,大约需要1秒的时间进行字符串替换。但由于我不知道可能会出现什么角色,所以我很高兴看到这样的表演。

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

基本上,删除不数字所有字符,一个字符串,它不为空。转换为十进制。除以100.返回结果。

适合我。