【问题标题】:ASP.NET MVC UpdateModel() parameter conversion woesASP.NET MVC UpdateModel() 参数转换问题
【发布时间】:2011-08-23 18:24:21
【问题描述】:

警告:我对 MVC 及其范式以及它的一些内部工作方式相对较新,但我对它很满意。这是我的第二个 MVC 应用程序,我对如何解决我们的一个测试人员发现的“问题”感到有些困惑。

用户得到的是一个编辑屏幕,给出了来自财政部的每日 LIBOR 利率(百分比)的生效日期。比率始终在 0 到 100 之间,因此我尝试在我的一个域对象的元数据中使用 RangeAttribute 来限制该范围。我已经指定了这样的范围:

[Required, DisplayName("Published Rate"), Range(typeof(decimal), "0", "100")]
public object PublishedRate { get; set; }

请注意,我正在传递字符串值,因为 RangeAttribute 没有采用小数的重载构造函数。在用户输入不同寻常的东西之前,这似乎很有效,例如:

“0.0000000000000000000000000000000001”

这会导致UpdateModel() 失败; ModelState 显示此错误(奇怪的是,相同的 ModelState 值出现了 3 次):

从“System.String”类型到“System.Decimal”类型的参数转换失败。

挖掘错误揭示了原因。下面的第一行是该字段的验证报告的内容。我很奇怪这并没有出现 model 验证错误(即没有出现在模型的摘要验证列表中):

“0.0000000000000000000000000000000001 不是十进制的有效值。” “对于 Decimal,值要么太大要么太小。”

System.Web.Mvc.ValueProviderResult.ConvertSimpleType()System.ComponentModel.BaseNumberConverter.ConvertFrom() 正在抛出异常。

用户永远不会输入这样的值,但我不介意知道 MVC 中是否有任何内置机制可以或将阻止这种情况(即服务器端)。像下面这样的数字似乎没有问题,它似乎只与非常小的数字不同。

“0.555555555555555555555555555555555”

归根结底,我真的只需要 9 位精度。支持这些值的数据库表列是decimal(9,6)。我知道我可以为我的模型实现一个自定义模型绑定器并手动从请求中收集值,但必须有一些更简单的东西,例如自定义 FilterAttribute 或可以在尝试之前更正值的东西绑定到模型,我只是不确定是什么,正在寻找建议。

我似乎记得读过有关尝试使用RangeAttribute 限制十进制值的一些问题,但我不记得这个问题了。也许您的 MVC 大师可以对这种情况有所了解。

【问题讨论】:

    标签: c# asp.net-mvc validation asp.net-mvc-2 data-annotations


    【解决方案1】:

    您可以使用 Regex 属性来包含精度为 9 的小数。这还允许您在 Regex 失败时添加自定义消息,例如“您的值可能在小数点后最多有 9 位。 "或类似的东西。此外,如果您启用了客户端验证,则正则表达式将同时在客户端和服务器端验证中工作。

    【讨论】:

      【解决方案2】:

      因此,经过几个小时的头撞后,我确定了以下用于小数的自定义模型活页夹。它确保所有十进制值在绑定之前都是可解析的。

      public class TreasuryIndexRateDecimalBinder : DefaultModelBinder
      {
          public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
          {
              var providerResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
              if (providerResult != null)
              {
                  decimal value;
                  if (!decimal.TryParse(providerResult.AttemptedValue, NumberStyles.Float, CultureInfo.CurrentCulture, out value))
                  {
                      // TODO: Decide whether to show an error
                      // bindingContext.ModelState.AddModelError(bindingContext.ModelName, "error message");
                      return 0m;
                  }
                  return Math.Round(value, 6);
              }
              return base.BindModel(controllerContext, bindingContext);
          }
      }
      

      Application_Start() 中设置了绑定,以便为所有十进制值注册它。

      protected void Application_Start()
      {
          ModelBinders.Binders.Add(typeof(decimal), new TreasuryIndexRateDecimalBinder());
      
          AreaRegistration.RegisterAllAreas();
          RegisterRoutes(RouteTable.Routes);
      }
      

      除非有人提出更有趣的方法,否则我想我会坚持下去。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多