Asp.Net MVC中带有千位分隔符的十进制值

13 asp.net-mvc decimal defaultmodelbinder

我有一个自定义模型类,其中包含一个十进制成员和一个接受此类的条目的视图.一切都运行良好,直到我添加javascripts来格式化输入控件内的数字.当焦点模糊时,格式代码格式化输入的数字与千位分隔符','.

问题是我的模态类中的十进制值没有与千位分隔符很好地绑定/解析.当我用"1,000.00"测试它时,ModelState.IsValid返回false,但是对于"100.00"没有任何更改它是有效的.

如果您有任何解决方案,请与我分享?

提前致谢.

样本类

public class Employee
{
    public string Name { get; set; }
    public decimal Salary { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

样本控制器

public class EmployeeController : Controller
{
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult New()
    {
        return View();
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult New(Employee e)
    {
        if (ModelState.IsValid) // <-- It is retruning false for values with ','
        {
            //Subsequence codes if entry is valid.
            //
        }
        return View(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

示例视图

<% using (Html.BeginForm())
   { %>

    Name:   <%= Html.TextBox("Name")%><br />
    Salary: <%= Html.TextBox("Salary")%><br />

    <button type="submit">Save</button>

<% } %>
Run Code Online (Sandbox Code Playgroud)

正如Alexander建议的那样,我尝试了使用Custom ModelBinder的解决方法.问题解决了.但是IDataErrorInfo实现的解决方案并不顺利.由于验证,输入0时Salary值变为null.请问有什么建议吗?Asp.Net MVC团队成员是否来到stackoverflow?我能从你那里得到一些帮助吗?

正如Alexander建议的那样,使用Custom Model Binder更新了代码

模型粘合剂

public class MyModelBinder : DefaultModelBinder {

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
        if (bindingContext == null) {
            throw new ArgumentNullException("bindingContext");
        }

        ValueProviderResult valueResult;
        bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName, out valueResult);
        if (valueResult != null) {
            if (bindingContext.ModelType == typeof(decimal)) {
                decimal decimalAttempt;

                decimalAttempt = Convert.ToDecimal(valueResult.AttemptedValue);

                return decimalAttempt;
            }
        }
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

员工类

    public class Employee : IDataErrorInfo {

    public string Name { get; set; }
    public decimal Salary { get; set; }

    #region IDataErrorInfo Members

    public string this[string columnName] {
        get {
            switch (columnName)
            {
                case "Salary": if (Salary <= 0) return "Invalid salary amount."; break;
            }
            return string.Empty;
        }
    }

    public string Error{
        get {
            return string.Empty;
        }
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*s F 15

其背后的原因是,在ValueProviderResult.cs中的ConvertSimpleType中使用了TypeConverter.

小数的TypeConverter不支持千位​​分隔符.在这里阅读:http://social.msdn.microsoft.com/forums/en-US/clr/thread/1c444dac-5d08-487d-9369-666d1b21706e

我还没有检查,但在那篇帖子中他们甚至说没有使用传入TypeConverter的CultureInfo.它永远是不变的.

           string decValue = "1,400.23";

        TypeConverter converter = TypeDescriptor.GetConverter(typeof(decimal));
        object convertedValue = converter.ConvertFrom(null /* context */, CultureInfo.InvariantCulture, decValue);
Run Code Online (Sandbox Code Playgroud)

所以我想你必须使用一种解决方法.不太好...


use*_*129 7

我不喜欢上面的解决方案,并提出了这个:

在我的自定义模型绑定器中,如果它是一个小数,我基本上用文化不变量值替换该值,然后将其余的工作移交给默认的模型绑定器.作为数组的rawvalue对我来说似乎很奇怪,但这是我在原始代码中看到/偷走的内容.

        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if(bindingContext.ModelType == typeof(decimal) || bindingContext.ModelType==typeof(Nullable<decimal>))
        {
            ValueProviderResult valueProviderResult = bindingContext.ValueProvider[bindingContext.ModelName];
            if (valueProviderResult != null)
            {
                decimal result;
                var array = valueProviderResult.RawValue as Array;
                object value;
                if (array != null && array.Length > 0)
                {
                    value = array.GetValue(0);
                    if (decimal.TryParse(value.ToString(), out result))
                    {
                        string val = result.ToString(CultureInfo.InvariantCulture.NumberFormat);
                        array.SetValue(val, 0);
                    }
                }
            }
        }
        return base.BindModel(controllerContext, bindingContext);
    }
Run Code Online (Sandbox Code Playgroud)


Fun*_*nka 4

似乎总是可以找到某种形式的解决方法,以使默认模型绑定器满意!我想知道您是否可以创建一个仅由模型绑定器使用的“伪”属性?(注意,这绝不是优雅的。我自己,我似乎越来越频繁地诉诸类似的技巧,只是因为它们有效并且它们“完成”了工作......)还要注意,如果您使用的是单独的“ViewModel”(我为此推荐),您可以将此代码放在那里,并使您的域模型保持干净整洁。

public class Employee
{
    private decimal _Salary;
    public string MvcSalary // yes, a string. Bind your form values to this!
    {
        get { return _Salary.ToString(); }
        set
        { 
            // (Using some pseudo-code here in this pseudo-property!)
            if (AppearsToBeValidDecimal(value)) {
                _Salary = StripCommas(value);
            }
        }
    }
    public decimal Salary
    {
        get { return _Salary; }
        set { _Salary = value; }
    }
}
Run Code Online (Sandbox Code Playgroud)

PS,我写完这篇文章后,我现在回头看,甚至在犹豫要不要发这个,太丑了!但如果你认为这可能有帮助,我会让你决定......

祝你好运!
-麦克风