C# Fluent验证不接受带有千位分隔符的数字

C# Fluent验证不接受带有千位分隔符的数字,c#,asp.net-mvc,fluentvalidation,C#,Asp.net Mvc,Fluentvalidation,我有一个ASP.NETMVC5项目,对MVC5进行了流畅的验证。我还使用jQuery屏蔽插件自动将数千个值添加到双倍值中 在模型中,我有: [Display(Name = "Turnover")] [DisplayFormat(ApplyFormatInEditMode = true,ConvertEmptyStringToNull =true,DataFormatString ="#,##0")] public double? Turnover { get; set;

我有一个ASP.NETMVC5项目,对MVC5进行了流畅的验证。我还使用jQuery屏蔽插件自动将数千个值添加到双倍值中

在模型中,我有:

    [Display(Name = "Turnover")]
    [DisplayFormat(ApplyFormatInEditMode = true,ConvertEmptyStringToNull =true,DataFormatString ="#,##0")]
    public double? Turnover { get; set; }
我认为:

<th class="col-xs-2">
    @Html.DisplayNameFor(model=>model.Turnover)
</th>
<td class="col-xs-4">
    @Html.TextBoxFor(model => model.Turnover, new { @class = "form-control number", placeholder="Enter number. Thousands added automatically" })
</td>
<td class="col-xs-6">
    @Html.ValidationMessageFor(model => model.Turnover, "", new { @class = "text-danger" })
</td>

@DisplayNameFor(model=>model.Overlover)
@Html.TextBoxFor(model=>model.overflower,新的{@class=“表单控制编号”,placeholder=“输入自动添加的编号”})
@Html.ValidationMessageFor(model=>model.overflower,“,new{@class=“text danger”})
为包含模型定义了fluent验证器,但它不包含任何规则。我只使用服务器端验证

public class MyModelValidator: AbstractValidator<MyModel>
{
    public MyModelValidator()
    {

    }
}
公共类MyModelValidator:AbstractValidator
{
公共MyModelValidator()
{
}
}
不幸的是,我得到的营业额验证错误如下:

我试着用它来解决这个问题。但是模型绑定器中的断点永远不会被命中-流畅的验证似乎会阻止值到达模型绑定器

有几件事值得一提:

  • 该问题与Fluent验证没有任何共同之处。无论是否经过流畅的验证,我都能够复制/修复它
  • 使用的
    DataFormatString
    不正确(缺少值占位符)。它应该是
    “{0:#,###0}”
  • 实际工作中使用的
    ModelBinder
    方法。我猜您忘记了它是为
    decimal
    数据类型编写的,而您的模型使用的是
    double?
    ,因此您必须为
    double
    double?
    类型编写并注册另一个
现在谈谈这个问题。实际上有两种解决办法。它们都使用以下帮助器类进行实际字符串转换:

using System;
using System.Collections.Generic;
using System.Globalization;

public static class NumericValueParser
{
    static readonly Dictionary<Type, Func<string, CultureInfo, object>> parsers = new Dictionary<Type, Func<string, CultureInfo, object>>
    {
        { typeof(byte), (s, c) => byte.Parse(s, NumberStyles.Any, c) },
        { typeof(sbyte), (s, c) => sbyte.Parse(s, NumberStyles.Any, c) },
        { typeof(short), (s, c) => short.Parse(s, NumberStyles.Any, c) },
        { typeof(ushort), (s, c) => ushort.Parse(s, NumberStyles.Any, c) },
        { typeof(int), (s, c) => int.Parse(s, NumberStyles.Any, c) },
        { typeof(uint), (s, c) => uint.Parse(s, NumberStyles.Any, c) },
        { typeof(long), (s, c) => long.Parse(s, NumberStyles.Any, c) },
        { typeof(ulong), (s, c) => ulong.Parse(s, NumberStyles.Any, c) },
        { typeof(float), (s, c) => float.Parse(s, NumberStyles.Any, c) },
        { typeof(double), (s, c) => double.Parse(s, NumberStyles.Any, c) },
        { typeof(decimal), (s, c) => decimal.Parse(s, NumberStyles.Any, c) },
    };

    public static IEnumerable<Type> Types { get { return parsers.Keys; } }

    public static object Parse(string value, Type type, CultureInfo culture)
    {
        return parsers[type](value, culture);
    }
}
您只需在
应用程序\u Start
中注册即可:

protected void Application_Start()
{
    NumericValueBinder.Register();  
    // ...
}
ModelBinders.Binders.Add(typeof(double), new DoubleModelBinder());
ModelBinders.Binders.Add(typeof(double?), new DoubleModelBinder());
自定义
类型转换器

这并不特定于ASP.NET MVC 5,但
DefaultModelBinder
将字符串转换委托给关联的(类似于其他NET UI框架)。事实上,这个问题是由以下事实引起的:数值类型的默认
TypeConverter
类不使用
Convert
类,而是
Parse
重载传递
NumberStyles.Float
,它排除了
NumberStyles.AllowThousands

幸运的是,
System.ComponentModel
提供了可扩展的功能,允许您关联自定义的
TypeConverter
。管道部分有点复杂(为了提供最终返回custom
TypeConverter
的实现,您必须注册一个custom),但是在提供的基类的帮助下,将大部分内容委托给基础对象,实现如下所示:

using System;
using System.ComponentModel;
using System.Globalization;

class NumericTypeDescriptionProvider : TypeDescriptionProvider
{
    public static void Register()
    {
        foreach (var type in NumericValueParser.Types)
            TypeDescriptor.AddProvider(new NumericTypeDescriptionProvider(type, TypeDescriptor.GetProvider(type)), type);
    }

    readonly Descriptor descriptor;

    private NumericTypeDescriptionProvider(Type type, TypeDescriptionProvider baseProvider)
        : base(baseProvider)
    {
        descriptor = new Descriptor(type, baseProvider.GetTypeDescriptor(type));
    }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    {
        return descriptor;
    }

    class Descriptor : CustomTypeDescriptor
    {
        readonly Converter converter;
        public Descriptor(Type type, ICustomTypeDescriptor baseDescriptor)
            : base(baseDescriptor)
        {
            converter = new Converter(type, baseDescriptor.GetConverter());
        }
        public override TypeConverter GetConverter()
        {
            return converter;
        }
    }

    class Converter : TypeConverter
    {
        readonly Type type;
        readonly TypeConverter baseConverter;
        public Converter(Type type, TypeConverter baseConverter)
        {
            this.type = type;
            this.baseConverter = baseConverter;
        }
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return baseConverter.CanConvertTo(context, destinationType);
        }
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            return baseConverter.ConvertTo(context, culture, value, destinationType);
        }
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return baseConverter.CanConvertFrom(context, sourceType);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value is string)
            {
                try { return NumericValueParser.Parse((string)value, type, culture); }
                catch { }
            }
            return baseConverter.ConvertFrom(context, culture, value);
        }
    }
}
(是的,为了添加一行基本代码,有很多样板代码!从另一方面来说,不需要处理可为空的类型,因为
DefaultModelBinder
已经这样做了:)

与第一种方法类似,您只需注册即可:

protected void Application_Start()
{
    NumericTypeDescriptionProvider.Register();  
    // ...
}

这可能是一个文化问题。尝试在客户端(10000000->10.000.000)使用点而不是逗号,或者在服务器端修复区域性问题。

问题不在于FluentValidation,而是MVC的模型绑定到
类型。MVC的默认模型绑定器无法解析数字并将
false
分配给
IsValid

在我包含以下代码后,问题得到了解决

并在
应用程序\u Start
中包含以下行:

protected void Application_Start()
{
    NumericValueBinder.Register();  
    // ...
}
ModelBinders.Binders.Add(typeof(double), new DoubleModelBinder());
ModelBinders.Binders.Add(typeof(double?), new DoubleModelBinder());

还考虑将当前的文化明确地声明为../p>。您是否考虑将类型更改为String,然后使用双字段的后退字段,然后让GETER和SETER为您进行转换?我觉得有一种更好的方式来处理它,就像在XAML应用程序中使用转换器一样,但我不确定在这种情况下如何处理它。。。或者从中提取一些东西:模型的FluentValidation在哪里?是来自

ModelState
的错误,还是您的AbstractValidator导致了错误?当您说我只使用服务器端验证时,我已经为空的验证器添加了代码-您是否确实禁用了客户端验证?如果您没有明确禁用它,那么
jquery.validate.js
将阻止提交。
@Html.ValidationMessageFor
看起来可疑-除了服务器端验证之外,为什么还要使用客户端验证?声明为
Nullable
overflower
属性接受不带千位分隔符的数字值,您需要使用JS设置客户端格式,而不更改字段值,或者删除相应属性的客户端验证。我尝试了所有这些技术,但无法使用FluentValidation。但就细节而言,我认为一半的赏金是合适的。我认为FluentValidation需要随着pull请求而改变。没问题,我不喜欢奖金。但听到这个问题没有得到解决,真的很难过。我安装了FluentValidation软件包并进行了设置,在这方面没有发现任何差异。如果没有上述挂钩,则问题重复。与他们-固定。可能还涉及到其他方面-例如,如果我使用
EditorFor
,上述修复不适用于
int
和类似的(但适用于
decimal
double
float
)。但是
TextBoxFor
适用于所有类型。也许这是个线索,我不知道。将会关注,如果您找到原因/解决方案,请发布。祝你好运谢谢-可能就是这样-需要将EditorFor切换到TextBoxFor。我现在可以使用了。我欠你125分的赏金,但似乎没有办法让我给你这个…工作,但不与integers@gls123Duh,您必须为
int
int?
编写另一个类,并将其添加到模型绑定器中。