我的视图模型中的某些decimal
和decimal?
属性被标记为“百分比”数据类型以及其他数据注释,例如:
[DataType("Percent")]
[Display(Name = "Percent of foo completed")]
[Range(0, 1)]
public decimal? FooPercent { get; set; }
我想让用户在输入数据方面有一些灵活性,即有或没有百分号,中间空格等。但我仍然希望使用DefaultModelBinder
行为来获取所有其功能,例如检查RangeAttribute
并添加适当的验证消息。
有没有办法解析并更改模型值,然后传递它?这是我正在尝试的,但我得到一个运行时异常。 (忽略实际的解析逻辑;这不是它的最终形式。我现在只对模型替换问题感兴趣。)
public class PercentModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
if (bindingContext.ModelMetadata.DataTypeName == "Percent")
{
ValueProviderResult result =
bindingContext.ValueProvider.GetValue(
bindingContext.ModelName);
if (result != null)
{
string stringValue =
(string)result.ConvertTo(typeof(string));
decimal decimalValue;
if (!string.IsNullOrWhiteSpace(stringValue) &&
decimal.TryParse(
stringValue.TrimEnd(new char[] { '%', ' ' }),
out decimalValue))
{
decimalValue /= 100.0m;
// EXCEPTION : This property setter is obsolete,
// because its value is derived from
// ModelMetadata.Model now.
bindingContext.Model = decimalValue;
}
}
}
return base.BindModel(controllerContext, bindingContext);
}
}
答案 0 :(得分:5)
没关系,这是对MVC循环中验证发生位置的基本误解。在MVC源代码中花了一些时间后,我看到它是如何工作的。
如果它对其他人有帮助,那么这就是为我工作的:
[DataType("Percent")]
[Display(Name = "Percent of foo completed")]
[Range(0.0d, 1.0d, ErrorMessage="The field {0} must be between {1:P0} and {2:P0}.")]
public decimal? FooPercent { get; set; }
在活页夹中,您只需返回值:
public class PercentModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
if (bindingContext.ModelMetadata.DataTypeName == "Percent")
{
ValueProviderResult result =
bindingContext.ValueProvider.GetValue(
bindingContext.ModelName);
if (result != null)
{
string stringValue =
(string)result.ConvertTo(typeof(string));
decimal decimalValue;
if (!string.IsNullOrWhiteSpace(stringValue) &&
decimal.TryParse(
stringValue.TrimEnd(new char[] { '%', ' ' }),
out decimalValue))
{
return decimalValue / 100.0m;
}
}
}
return base.BindModel(controllerContext, bindingContext);
}
}