在 C# 中实现自定义模型验证,主要靠
DataAnnotations特性 + 实现
IValidatableObject接口两种方式配合使用。前者适合字段级简单规则(如非空、长度、格式),后者用于跨字段、业务逻辑强相关的复合校验(比如“结束时间不能早于开始时间”)。
用 DataAnnotations 做基础字段验证
这是最常用、最直观的方式。给模型属性打上内置特性,框架(如 ASP.NET Core MVC、Entity Framework)会自动触发验证。
[Required]:标记必填项,支持自定义错误消息:[Required(ErrorMessage = "用户名不能为空")][StringLength(50, MinimumLength = 2)]:限制字符串长度范围 [EmailAddress]、[Phone]、[Range(18, 65)]:语义化验证,开箱即用 自定义特性需继承
ValidationAttribute,重写
IsValid方法(支持服务注入时建议用
IValidationAttributeAdapterProvider配合)
用 IValidatableObject 做跨字段/业务级验证
当验证逻辑涉及多个属性、需要访问外部服务或执行复杂判断时,让模型实现
IValidatableObject接口更合适。它提供一个
Validate方法,在所有字段级验证通过后被调用。 实现方式:在模型类中添加
public IEnumerable<validationresult> Validate(ValidationContext validationContext)</validationresult>返回
ValidationResult.Success表示通过;否则用
new ValidationResult("错误信息", new[] { "关联属性名" }) 返回失败项
例如:检查 StartDate和
EndDate是否合法:
if (EndDate yield return new ValidationResult("结束时间不能早于开始时间", new[] { nameof(EndDate) }); 注意:该方法不会自动触发字段级验证,它只补充校验——字段级失败时,
Validate通常不执行(取决于具体上下文,如 MVC 默认跳过)
两者结合使用的典型场景
实际开发中,推荐分层验证:用
DataAnnotations拦住明显非法输入(空值、超长、格式错),再用
IValidatableObject处理依赖关系和业务约束。 例如用户注册模型:
[Required]控制昵称、邮箱必填;
[EmailAddress]校验邮箱格式;
IValidatableObject则检查“密码与确认密码是否一致”、“邀请码是否有效”、“手机号是否已被注册”(需注入服务) 若需在
Validate中调用 DI 服务(如数据库查重),可通过
validationContext.GetService<iuserservice>()</iuserservice>获取(ASP.NET Core 3.0+ 支持) 验证结果统一由
ModelState(Web)或
Validator.TryValidateObject(手动触发)收集,无需额外适配
验证的触发与调试小提示
验证不是自动发生的,得靠框架或手动调用才生效。
ASP.NET Core MVC 中,参数绑定后自动验证,失败则ModelState.IsValid为
false手动验证可用:
Validator.TryValidateObject(model, new ValidationContext(model), results, true)(
true表示验证所有属性,包括私有) 调试时打印
results可看到全部错误;也可在
Validate方法里加断点,确认逻辑是否执行 避免在
Validate中抛异常——应返回
ValidationResult,否则可能中断流程
基本上就这些。DataAnnotations 负责“能不能输”,IValidatableObject 负责“合不合理”,搭配着用,验证既清晰又灵活。
