ASP.NET MVC绑定十进制值 [英] ASP.NET MVC binding decimal value

查看:73
本文介绍了ASP.NET MVC绑定十进制值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图弄清楚为什么框架拒绝将"1,234.00"值绑定到十进制.可能是什么原因?

I'm trying to figure out why framework refuses to bind "1,234.00" value to decimal. What can be the reason for it?

"123.00"或"123.0000"之类的值成功绑定.

Values like "123.00" or "123.0000" bind successfully.

我有以下代码在Global.asax中设置文化配置

I have the following code setting my culture config in Global.asax

    public void Application_AcquireRequestState(object sender, EventArgs e)
    {
        var culture = (CultureInfo)Thread.CurrentThread.CurrentCulture.Clone();
        culture.NumberFormat.NumberDecimalSeparator = culture.NumberFormat.CurrencyDecimalSeparator = culture.NumberFormat.PercentDecimalSeparator = ".";
        culture.NumberFormat.NumberGroupSeparator = culture.NumberFormat.CurrencyGroupSeparator = culture.NumberFormat.PercentGroupSeparator = ",";
        Thread.CurrentThread.CurrentCulture = culture;
    }

将法语区域性设置为Web.Config中的默认区域性

French culture is set as default culture in Web.Config

  <globalization uiCulture="fr-FR" culture="fr-FR" />

我已经深入研究了System.Web.Mvc.dll的ValueProviderResult类的源.它使用System.ComponentModel.DecimalConverter.

I've dived into sources of System.Web.Mvc.dll's ValueProviderResult class. It is using System.ComponentModel.DecimalConverter.

converter.ConvertFrom((ITypeDescriptorContext) null, culture, value)

此处是消息"1,234.0000不是十进制的有效值"的地方.来自.

Here is where the message "1,234.0000 is not a valid value for Decimal." comes from.

我试图在操场上运行以下代码:

I've tried to run the following code in my playground:

static void Main()
{
    var decConverter = TypeDescriptor.GetConverter(typeof(decimal));
    var culture = new CultureInfo("fr-FR");
    culture.NumberFormat.NumberDecimalSeparator = culture.NumberFormat.CurrencyDecimalSeparator = culture.NumberFormat.PercentDecimalSeparator = ".";
    culture.NumberFormat.NumberGroupSeparator = culture.NumberFormat.CurrencyGroupSeparator = culture.NumberFormat.PercentGroupSeparator = ",";
    Thread.CurrentThread.CurrentCulture = culture;
    var d1 = Decimal.Parse("1,232.000");
    Console.Write("{0}", d1);  // prints  1234.000     
    var d2 = decConverter.ConvertFrom((ITypeDescriptorContext)null, culture, "1,232.000"); // throws "1,234.0000 is not a valid value for Decimal."
    Console.Write("{0}", d2);
}

DecimalConverter引发相同的异常. Decimal.Parse正确解析相同的字符串.

DecimalConverter throws same exception. Decimal.Parse correctly parses the same string.

推荐答案

问题是,DecimalConverter.ConvertFrom不支持

The problem is, that DecimalConverter.ConvertFrom does not support the AllowThousands flag of the NumberStyles enumeration when it calls Number.Parse. The good news is, that there exists a way to "teach" it to do so!

Decimal.Parse内部调用Number.Parse,其数字样式设置为Number,为此将AllowThousands标志设置为true.

Decimal.Parse internally calls Number.Parse with the number style set to Number, for which the AllowThousands flag is set to true.

[__DynamicallyInvokable]
public static decimal Parse(string s)
{
    return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo);
}

从描述符接收类型转换器时,实际上会得到DecimalConverter的实例. ConvertFrom方法有点通用,所以在这里我只引用当前场景的相关部分.缺少的部分正在实现对十六进制字符串和异常处理的支持. 1

When you are receiving a type converter from the descriptor, you actually get an instance of DecimalConverter. The ConvertFrom method is a kinda general and large, so I only quote the relevant parts for the current scenario here. The missing parts are implementing support for hex strings and exception handling.1

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
    if (value is string) 
    {
        // ...

        string text = ((string)value).Trim();

        if (culture == null) 
            culture = CultureInfo.CurrentCulture;

        NumberFormatInfo formatInfo = (NumberFormatInfo)culture.GetFormat(typeof(NumberFormatInfo));
        return FromString(text, formatInfo);

        // ...
    }

    return base.ConvertFrom(context, culture, value);
}

DecimalConverter还会覆盖FromString实现,并且出现问题:

DecimalConverter also overwrites the FromString implementation and there the problem raises:

internal override object FromString(string value, NumberFormatInfo formatInfo) 
{
    return Decimal.Parse(value, NumberStyles.Float, formatInfo);
}

在数字样式设置为Float的情况下,AllowThousands标志设置为false!但是,您可以编写包含几行代码的自定义转换器,以解决此问题.

With the number style set to Float, the AllowThousands flag is set to false! However you can write a custom converter with a few lines of code that fixes this issue.

class NumericDecimalConverter : DecimalConverter
{
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string)
        {
            string text = ((string)value).Trim();

            if (culture == null) 
                culture = CultureInfo.CurrentCulture;

            NumberFormatInfo formatInfo = (NumberFormatInfo)culture.GetFormat(typeof(NumberFormatInfo));
            return Decimal.Parse(text, NumberStyles.Number, formatInfo);
        }
        else
        {
            return base.ConvertFrom(value);
        }
    }
}

1 请注意,该代码看起来与原始实现相似.如果您需要未引用"的东西,则将其直接委派给base或自行实现.您可以使用ILSpy/DotPeek/etc查看实施.或通过从Visual Studio对其进行调试.

1Note that the code looks similar to the original implementation. If you need the "unquoted" stuff either delegate it directly to base or implement it on your own. You can view the implementation using ILSpy/DotPeek/etc. or by debugging into them from Visual Studio.

最后,在Reflection的帮助下,您可以将Decimal的类型转换器设置为使用新的自定义转换器!

Finally, with a little help from Reflection, you can set the type converter for Decimal to use your new custom one!

TypeDescriptor.AddAttributes(typeof(decimal), new TypeConverterAttribute(typeof(NumericDecimalConverter)));

这篇关于ASP.NET MVC绑定十进制值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆