簡體   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