如何解决十进制的DataAnnotations百分比
我在c#.netcore剃须刀页面应用程序中的数据对象上的字段中使用以下DataAnnotations:
[Display(Name = "Markup For Profit")]
[Required]
[DisplayFormat(DataFormatString = "{0:P0}",ApplyFormatInEditMode = true)]
public double ProfitMarkup { get; set; }
在“剃刀”页面上,我在输入中使用以下字段:
<input asp-for="ProfitMarkup" class="form-control" autocomplete="off" />
ProfitMarkup的十进制值正确显示(即0.2显示为20%),这很好。但是,当我去提交表单时,客户端验证会引发错误:
“利润标记字段必须是数字。”
我敢肯定,这是因为显示格式已添加了'%'符号。
允许20%的页面输入到达对象并将小数设置为0.2的最佳方法是什么?
解决方法
当DataAnnotation的输入框中有百分号时,默认的ModelBinder将无法获取正确的发布值。您可以创建一个自定义ModelBinder。使用ASP.Net MVC的示例:
// The ViewModel
public class HomeViewModel
{
[Display(Name = "Markup For Profit")]
[Required]
[DisplayFormat(DataFormatString = "{0:P2}",ApplyFormatInEditMode =true)]
public double ProfitMarkup { get; set; }
}
// Two Classes to implement custom ModelBinder
public class DoublePercentDataBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(HomeViewModel))
{
HttpRequestBase request = controllerContext.HttpContext.Request;
string pMarkup = request.Form.Get("ProfitMarkup").TrimEnd('%');
return new HomeViewModel
{
ProfitMarkup = Double.Parse(pMarkup)/100
};
}
else
{
return base.BindModel(controllerContext,bindingContext);
}
}
}
public class DoublePercentBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,ModelBindingContext bindingContext)
{
HttpRequestBase request = controllerContext.HttpContext.Request;
string pMarkup = request.Form.Get("ProfitMarkup").TrimEnd('%');
return new HomeViewModel
{
ProfitMarkup = Double.Parse(pMarkup)/100
};
}
}
// Attach it in Application_Start in Global.asax
protected void Application_Start()
{
ModelBinders.Binders.Add(typeof(HomeViewModel),new DoublePercentBinder());
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
// HomeController Get and Post
public class HomeController : Controller
{
public ActionResult Index()
{
HomeViewModel vm = new HomeViewModel();
vm.ProfitMarkup = 1.2;
return View(vm);
}
[HttpPost]
public ActionResult Index([ModelBinder(typeof(DoublePercentBinder))] HomeViewModel hvm)
{
return View(hvm);
}
// The Razor View
@using (Html.BeginForm("Index","Home",FormMethod.Post))
{
<div>
@Html.EditorFor(x => x.ProfitMarkup)
@Html.ValidationMessageFor(x => x.ProfitMarkup)
</div>
<br />
<input type="submit" value="Submit" />
}
,
添加新的百分比类型和PercentageConverter TypeConverter为我解决了此问题。
我已声明一个新属性,该属性读取如下所示的double ProfitMarkup属性:
[Display(Name = "Markup For Profit")]
[Required]
[NotMapped]
public Percentage ProfitMarkupPercentage { get { return new Percentage(ProfitMarkup); } set { ProfitMarkup = value; } }
ProfitMarkup以双精度形式写入数据库/从数据库写入,并且ProfitMarkupPercentage像这样在剃刀页面上显示:
<input asp-for="ProfitMarkupPercentage" class="form-control" autocomplete="off" />
百分比对象及其TypeConverter的代码为(此响应由该线程中的响应提供:How to convert percentage string to double?):
[TypeConverter(typeof(PercentageConverter))]
public struct Percentage
{
public double Value;
public Percentage(double value)
{
Value = value;
}
static public implicit operator double(Percentage pct)
{
return pct.Value;
}
static public implicit operator string(Percentage pct) { return pct.ToString(); }
public Percentage(string value)
{
Value = 0.0;
var pct = (Percentage)TypeDescriptor.GetConverter(GetType()).ConvertFromString(value);
Value = pct.Value;
}
public override string ToString()
{
return ToString(CultureInfo.InvariantCulture);
}
public string ToString(CultureInfo Culture)
{
return TypeDescriptor.GetConverter(GetType()).ConvertToString(null,Culture,this);
}
}
public class PercentageConverter : TypeConverter
{
static TypeConverter conv = TypeDescriptor.GetConverter(typeof(double));
public override bool CanConvertFrom(ITypeDescriptorContext context,Type sourceType)
{
return conv.CanConvertFrom(context,sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context,Type destinationType)
{
if (destinationType == typeof(Percentage))
{
return true;
}
return conv.CanConvertTo(context,destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context,System.Globalization.CultureInfo culture,object value)
{
if (value == null)
{
return new Percentage();
}
if (value is string)
{
string s = value as string;
s = s.TrimEnd(' ','\t','\r','\n');
var percentage = s.EndsWith(culture.NumberFormat.PercentSymbol);
if (percentage)
{
s = s.Substring(0,s.Length - culture.NumberFormat.PercentSymbol.Length);
}
double result = (double)conv.ConvertFromString(s);
if (percentage)
{
result /= 100;
}
return new Percentage(result);
}
return new Percentage((double)conv.ConvertFrom(context,culture,value));
}
public override object ConvertTo(ITypeDescriptorContext context,CultureInfo culture,object value,Type destinationType)
{
if (!(value is Percentage))
{
throw new ArgumentNullException("value");
}
var pct = (Percentage)value;
if (destinationType == typeof(string))
{
return conv.ConvertTo(context,pct.Value * 100,destinationType) + culture.NumberFormat.PercentSymbol;
}
return conv.ConvertTo(context,pct.Value,destinationType);
}
}
默认模型绑定器现在正确地在属性和剃须刀页面之间来回填充。
您还可以使用正则表达式将验证数据注释添加到ProfitMarkupPercentage属性:
[RegularExpression(@"^(0*100{1,1}\.?((?<=\.)0*)?%?$)|(^0*\d{0,2}\.?((?<=\.)\d*)?%?)$",ErrorMessage = "Invalid percentage")]
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。