23

我正在使用NerdDinner应用程序,试图自学 ASP.NET MVC。但是,我偶然发现了一个全球化问题,我的服务器以逗号作为小数分隔符显示浮点数,但虚拟地球地图要求它们带有点,这会导致一些问题。

我已经在我的视图中解决了映射 JavaScript 的问题,但是如果我现在尝试使用点作为小数分隔符发布已编辑的晚餐条目,则控制器InvalidOperationException在更新模型(在方法中)时会失败(抛出UpdateModel())。我觉得我也必须在控制器的某个地方设置适当的文化,我尝试过,OnActionExecuting()但没有帮助。

4

4 回答 4

65

我刚刚在一个真实的项目中重新审视了这个问题,终于找到了一个可行的解决方案。正确的解决方案是为该类型提供一个自定义模型绑定器decimaldecimal?如果您正在使用它们):

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;

        // in decimal? binding attemptedValue can be Null
        if (attemptedValue != null)
        {
            // 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, StringComparison.Ordinal) == -1
                && attemptedValue.IndexOf(alternateSeperator, StringComparison.Ordinal) != -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;
    }
}

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

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

请注意,代码不是我的,我实际上是在 Kristof Neirynck 的博客上找到的。我刚刚编辑了几行并添加了特定数据类型的活页夹,而不是替换默认活页夹。

于 2011-02-25T12:51:14.377 回答
7

在你的 web.config 中设置它

  <system.web>
    <globalization uiCulture="en" culture="en-US" />

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

于 2009-04-27T13:32:23.297 回答
0

你能用不变的文化解析文本吗?抱歉,我的前面没有 NerdDinner 代码,但是如果你传递点分隔的小数,那么如果你告诉它使用不变的文化,解析应该没问题. 例如

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

编辑. 顺便说一句,我怀疑这是 NerdDinner 代码中的一个错误,与您之前的问题相同。

于 2009-04-27T14:03:17.577 回答
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。返回结果。

为我工作。

于 2015-11-11T20:49:54.230 回答