繁体   English   中英

ASP.NET MVC绑定十进制值

[英]ASP.NET MVC binding decimal value

我试图弄清楚为什么框架拒绝将“ 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调用Number.Parse时不支持NumberStyles枚举的AllowThousands标志。 好消息是,有一种方法可以“教”它!

Decimal.Parse内部调用Number.Parse ,其数字样式设置为Number ,其AllowThousands标志设置为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);
}

在数字样式设置为FloatAllowThousands标志设置为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 / etc查看实施。 或通过从Visual Studio对其进行调试。

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

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

根据Phil Haack 在此处发表的有关十进制模型绑定的文章的评论,我相信“为什么”的部分答案是浏览器中的区域性很复杂,您不能保证应用程序的区域性是相同的区域性用户/浏览器用于小数的设置。 无论如何,这都是已知的“问题”,并且除了提供以下解决方案外,还曾提出过类似的问题,并提供了多种解决方案: 接受逗号和点作为小数点分隔符,以及如何在ASP.NET MVC控制器中设置小数点分隔符? 例如。

您可以尝试覆盖DefaultModelBinder。 让我知道这是否无效,我将删除此帖子。 我实际上并没有组合MVC应用程序并对其进行测试,但是根据经验,这应该可行:

public class CustomModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
    {
        if(propertyDescriptor.PropertyType == typeof(decimal))
        {
            propertyDescriptor.SetValue(bindingContext.Model, double.Parse(propertyDescriptor.GetValue(bindingContext.Model).ToString()));
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
        else
        {
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
    }
}

这里的问题似乎是应用于Decimal.Parse(string)的默认数字样式

从MSDN文档

其余的各个字段标志定义样式元素,这些样式元素可以但不必须以十进制数字的字符串表示形式存在,以使解析操作成功。

所以这意味着下面的d1和d2都可以成功解析

                var d1 = Decimal.Parse("1,232.000");

                var d2 = Decimal.Parse("1,232.000", NumberStyles.Any);

但是,在应用类型转换器时,似乎基本上只允许允许训练空间,允许小数点和允许前导符号。 因此,下面的d3 express将抛出运行时错误

                var d3 = Decimal.Parse("1,232.000", NumberStyles.AllowLeadingSign | NumberStyles.AllowLeadingWhite | 
                                        NumberStyles.AllowTrailingWhite | NumberStyles.AllowDecimalPoint);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM