十进制的DataAnnotations百分比

如何解决十进制的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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-