如何在 ASP.NET MVC 控制器中设置小数点分隔符?

发布于 2024-07-18 05:56:29 字数 355 浏览 4 评论 0原文

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

我已经解决了映射 JavaScript 的问题在我看来,但如果我现在尝试发布一个编辑后的晚餐条目,并用点作为小数点分隔符,则在更新模型时(在 UpdateModel( ) 方法)。 我觉得我也必须在控制器中的某个地方设置正确的区域性,我在 OnActionExecuting() 中尝试过,但这没有帮助。

I'm working with the NerdDinner application trying to teach myself ASP.NET MVC. However, I have stumbled upon a problem with globalization, where my server presents floating point numbers with a comma as the decimal separator, but Virtual Earth map requires them with dots, which causes some problems.

I have already solved the issue with the mapping JavaScript in my views, but if I now try to post an edited dinner entry with dots as decimal separators the controller fails (throwing InvalidOperationException) when updating the model (in the UpdateModel() metod). I feel like I must set the proper culture somewhere in the controller as well, I tried it in OnActionExecuting() but that didn't help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

灼痛 2024-07-25 05:56:29

我刚刚在实际项目中重新审视了这个问题,终于找到了一个可行的解决方案。 正确的解决方案是为 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;

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

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

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

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

I have just revisited the issue in a real project and finally found a working solution. Proper solution is to have a custom model binder for the type decimal (and decimal? if you're using them):

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

Then in Global.asax.cs in Application_Start():

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

Note that code is not mine, I actually found it at Kristof Neirynck's blog here. I just edited a few lines and am adding the binder for a specific data type, not replacing the default binder.

月下客 2024-07-25 05:56:29

在 web.config 中进行设置

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

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

Set this in your web.config

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

You appear to be using a server that is setup with a language that uses comma's instead of decimal places. You can adjust the culture to one that uses the comma's in a way that your application is designed, such as en-US.

城歌 2024-07-25 05:56:29

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

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

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

Can you parse the text using the invariant culture - sorry, I don't have the NerdDinner code in fornt of me, but if you are passing in dot-separated decimals than the parsing should be OK if you tell it to use the invariant culture. E.g.

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

Edit. I suspect that this is a bug in the NerdDinner code by the way, along the same lines as your previous problem.

情感失落者 2024-07-25 05:56:29

我对此有不同的看法,你可能会喜欢。 我不喜欢接受的答案是它不检查其他字符。 我知道在某些情况下,货币符号会出现在框中,因为我的用户不太了解。 所以是的,我可以检查 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。返回结果。

对我有用。

I have a different take on this, you might like it. What I don't like about the accepted answer is it doesn't check for other characters. I know there will be a case where the currency symbol will be in the box because my user doesn't know better. So yeah I can check in javascript to remove it, but what if for some reason javascript isn't on? Then extra characters might get through. Or if someone tries to spam you passing unknown characters through... who knows! So I decided to use a regex. It's a bit slower, tiny fraction slower - for my case it was 1,000,000 iterations of the regex took just under 3 seconds, while around 1 second to do a string replace on a coma and period. But seeing as I don't know what characters might come through, then I am happy for this slightest of performance hits.

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

Basically, remove all characters that are not digits, for a string that isn't empty. Convert to decimal. Divide by 100. Return result.

Works for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文