ASP.NET MVC绑定十进制值 [英] ASP.NET MVC binding decimal value
问题描述
我试图弄清楚为什么框架拒绝将"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屋!