ASP.NETMVC如何绑定十进制值?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (21)

我试图找出为什么框架拒绝将“1,234.00”值绑定到十进制。可能是什么原因呢?

像“123.00”或“123.0000”这样的值成功绑定。

我有以下代码在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中被设置为默认文化

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

我已经深入System.Web.Mvc.dll的ValueProviderResult类的来源。它使用System.ComponentModel.DecimalConverter。

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

这里是消息“1,234.0000不是十进制的有效值”。来自。

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

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.ConvertFrom不支持调用时AllowThousandsNumberStyles枚举标志Number.Parse。好消息是,有一种方法可以“教”它这样做!

Decimal.Parse内部调用Number.Parse的数字样式设置为NumberAllowThousands标志设置为true。

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

当你从描述符中接收到一个类型转换器时,你实际上得到了一个实例DecimalConverter。该ConvertFrom方法是一个普遍的和大的,所以我只在这里引用当前场景的相关部分。缺少的部分正在实现对十六进制字符串和异常处理的支持。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实现,并提出问题:

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

将数字样式设置为Float,该AllowThousands标志设置为false!但是,可以使用几行代码来编写自定义转换器,以修复此问题。

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 /等查看实现。或者通过从Visual Studio中调试它们。

最后,借助Reflection的一些帮助,可以设置类型转换器Decimal以使用新定制转换器!

TypeDescriptor.AddAttributes(typeof(decimal), new TypeConverterAttribute(typeof(NumericDecimalConverter)));
用户回答回答于

基于约小数模型由Phil Haack结合的文章评论在这里,我相信答案的一部分,“为什么”是文化在浏览器中是复杂的,你不能保证你的应用程序的文化将是相同的文化用户/浏览器使用的设置为小数。

扫码关注云+社区