You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义类型如何使用.NET框架标准验证数据注解?

在.NET中对自定义类型使用标准数据注解的解决方案

当然可以实现让StringLength、RegularExpression、Required这类标准数据注解作用于自定义类型的Value属性,但你遇到的异常其实是有原因的:这些标准验证属性内部会严格检查目标值的类型——比如StringLengthAttribute的IsValid方法会先尝试将值转为string,如果转换失败(自定义类型显然不是string),就直接抛出ArgumentException,重写ToString()根本走不到后续的验证逻辑。

下面提供几种可行的解决方案,你可以根据自己的场景选择:

方法一:自定义验证属性(简单直接,针对性强)

创建继承自标准验证属性的自定义版本,重写IsValid方法来处理你的自定义类型,提取Value属性进行验证。

比如针对StringLengthAttribute:

public class CustomStringLengthAttribute : StringLengthAttribute
{
    public CustomStringLengthAttribute(int maximumLength) : base(maximumLength) { }

    public override bool IsValid(object value)
    {
        // 处理自定义类型,这里假设你的自定义类型叫MyCustomType
        if (value is MyCustomType customObj)
        {
            return base.IsValid(customObj.Value);
        }
        // 其他类型按默认逻辑处理
        return base.IsValid(value);
    }
}

用同样的方式封装RegularExpressionAttribute和RequiredAttribute:

// 自定义Required验证
public class CustomRequiredAttribute : RequiredAttribute
{
    public override bool IsValid(object value)
    {
        if (value is MyCustomType customObj)
        {
            return base.IsValid(customObj.Value);
        }
        return base.IsValid(value);
    }
}

// 自定义正则验证
public class CustomRegularExpressionAttribute : RegularExpressionAttribute
{
    public CustomRegularExpressionAttribute(string pattern) : base(pattern) { }

    public override bool IsValid(object value)
    {
        if (value is MyCustomType customObj)
        {
            return base.IsValid(customObj.Value);
        }
        return base.IsValid(value);
    }
}

之后在模型中直接使用这些自定义注解即可:

public class MyModel
{
    [CustomRequired]
    [CustomStringLength(10)]
    [CustomRegularExpression(@"^\w+$")]
    public MyCustomType MyProperty { get; set; }
}

优点:代码简单,不需要全局配置,只针对需要的自定义类型处理;缺点:每个标准注解都要写对应的自定义版本,扩展性一般。

方法二:自定义模型验证提供者(通用灵活,适合多场景)

如果你的项目中有多个自定义类型需要支持标准注解,或者不想重复写自定义属性,可以通过自定义ModelValidatorProvider来全局拦截验证逻辑,自动提取自定义类型的Value属性进行验证。

1. 创建自定义验证提供者

public class CustomTypeValidatorProvider : DataAnnotationsModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        // 只处理目标类型为MyCustomType的情况
        if (metadata.ModelType == typeof(MyCustomType))
        {
            var validators = new List<ModelValidator>();
            foreach (var attr in attributes)
            {
                // 根据不同的标准注解创建对应的自定义验证器
                switch (attr)
                {
                    case StringLengthAttribute strAttr:
                        validators.Add(new CustomTypeStringLengthValidator(metadata, context, strAttr));
                        break;
                    case RegularExpressionAttribute regexAttr:
                        validators.Add(new CustomTypeRegexValidator(metadata, context, regexAttr));
                        break;
                    case RequiredAttribute reqAttr:
                        validators.Add(new CustomTypeRequiredValidator(metadata, context, reqAttr));
                        break;
                    // 其他需要支持的验证属性可以继续添加
                    default:
                        // 不处理的属性用默认验证器
                        validators.AddRange(base.GetValidators(metadata, context, new[] { attr }));
                        break;
                }
            }
            return validators;
        }
        // 非自定义类型按默认逻辑处理
        return base.GetValidators(metadata, context, attributes);
    }
}

2. 实现对应的验证器

以StringLength的验证器为例:

public class CustomTypeStringLengthValidator : DataAnnotationsModelValidator<StringLengthAttribute>
{
    public CustomTypeStringLengthValidator(ModelMetadata metadata, ControllerContext context, StringLengthAttribute attribute)
        : base(metadata, context, attribute) { }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        var customObj = Metadata.Model as MyCustomType;
        if (customObj != null)
        {
            // 验证自定义类型的Value属性
            if (!Attribute.IsValid(customObj.Value))
            {
                yield return new ModelValidationResult { Message = ErrorMessage };
            }
        }
        else
        {
            // 非自定义类型按默认逻辑验证
            foreach (var result in base.Validate(container))
            {
                yield return result;
            }
        }
    }
}

Required和RegularExpression的验证器写法类似,只需要替换对应的属性类型和验证逻辑即可。

3. 注册验证提供者

在项目启动时替换默认的验证提供者:

// ASP.NET MVC(Global.asax.cs)
protected void Application_Start()
{
    // 移除默认的DataAnnotations验证提供者
    var defaultProvider = ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().First();
    ModelValidatorProviders.Providers.Remove(defaultProvider);
    // 添加自定义验证提供者
    ModelValidatorProviders.Providers.Add(new CustomTypeValidatorProvider());
    
    // 其他启动配置...
}

// ASP.NET Core(Startup.cs)
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        var defaultProvider = options.ModelValidatorProviders.OfType<DataAnnotationsModelValidatorProvider>().First();
        options.ModelValidatorProviders.Remove(defaultProvider);
        options.ModelValidatorProviders.Add(new CustomTypeValidatorProvider());
    });
}

优点:全局生效,一次配置支持所有自定义类型的标准注解验证;缺点:需要理解模型验证的底层逻辑,代码量稍大。

方法三:使用TypeConverter(适合ASP.NET Core,结合模型绑定)

如果是在ASP.NET Core中,还可以通过实现TypeConverter,让自定义类型可以和string互相转换,这样标准验证属性就能自动识别并验证Value属性。

1. 实现TypeConverter

public class MyCustomTypeConverter : TypeConverter
{
    // 支持从string转换为自定义类型
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string strValue)
        {
            return new MyCustomType { Value = strValue };
        }
        return base.ConvertFrom(context, culture, value);
    }

    // 支持从自定义类型转换为string
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string) && value is MyCustomType customObj)
        {
            return customObj.Value;
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

2. 给自定义类型添加TypeConverterAttribute

[TypeConverter(typeof(MyCustomTypeConverter))]
public class MyCustomType
{
    public string Value { get; set; }
}

之后就可以直接在模型中使用标准注解了:

public class MyModel
{
    [Required]
    [StringLength(10)]
    [RegularExpression(@"^\w+$")]
    public MyCustomType MyProperty { get; set; }
}

优点:不需要修改验证属性,直接复用标准注解;缺点:依赖模型绑定的类型转换逻辑,只适合ASP.NET Core场景,对复杂自定义类型的支持有限。


内容的提问来源于stack exchange,提问作者Jake Shakesworth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:37:30