在C#的Web开发或者业务层开发中,模型验证是数据进入系统前的第一道关卡,默认的验证规则往往无法满足复杂的业务需求,这时候就需要通过自定义验证来保证数据的合法性。DataAnnotations和IValidatableObject是C#中两种主流的自定义模型验证方式,各自适用于不同的场景。

DataAnnotations自定义验证
DataAnnotations是.NET框架提供的特性标注验证方式,除了内置的Required、Range等特性外,我们还可以通过继承ValidationAttribute基类来自定义验证特性,实现个性化的校验规则。
自定义ValidationAttribute
自定义验证特性需要重写IsValid方法,该方法接收要验证的值和验证上下文,返回验证结果。下面是一个校验手机号格式的自定义特性示例:
using System;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
// 自定义手机号验证特性
public class PhoneValidationAttribute : ValidationAttribute
{
// 手机号正则规则
private const string PhonePattern = @"^1[3-9]d{9}$";
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// 如果值为空,交给Required特性处理,当前特性不做校验
if (value == null)
{
return ValidationResult.Success;
}
string phone = value.ToString();
// 正则匹配校验
if (Regex.IsMatch(phone, PhonePattern))
{
return ValidationResult.Success;
}
// 校验失败,返回错误信息,错误成员名称为当前属性名
return new ValidationResult(ErrorMessage ?? "手机号格式不正确", new[] { validationContext.MemberName });
}
}
使用自定义DataAnnotations特性
定义好特性后,只需要在模型的对应属性上标注即可,和使用内置特性没有区别:
using System.ComponentModel.DataAnnotations;
public class UserRegisterModel
{
[Required(ErrorMessage = "用户名不能为空")]
public string UserName { get; set; }
[Required(ErrorMessage = "手机号不能为空")]
[PhoneValidation(ErrorMessage = "请输入正确的手机号")]
public string Phone { get; set; }
[Required(ErrorMessage = "密码不能为空")]
[MinLength(6, ErrorMessage = "密码长度不能小于6位")]
public string Password { get; set; }
}
IValidatableObject接口自定义验证
当验证规则需要依赖多个属性,或者需要复杂的业务逻辑判断时,DataAnnotations单个特性的方式就不够灵活了,这时候可以实现IValidatableObject接口,在模型内部编写整体验证逻辑。
实现IValidatableObject接口
IValidatableObject接口只有一个Validate方法,该方法接收验证上下文,返回一组验证结果。下面是一个校验密码和确认密码是否一致的示例:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class UserRegisterModel : IValidatableObject
{
[Required(ErrorMessage = "用户名不能为空")]
public string UserName { get; set; }
[Required(ErrorMessage = "手机号不能为空")]
public string Phone { get; set; }
[Required(ErrorMessage = "密码不能为空")]
public string Password { get; set; }
[Required(ErrorMessage = "确认密码不能为空")]
public string ConfirmPassword { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// 校验密码和确认密码是否一致
if (!string.IsNullOrEmpty(Password) && !string.IsNullOrEmpty(ConfirmPassword))
{
if (Password != ConfirmPassword)
{
yield return new ValidationResult("密码和确认密码不一致", new[] { nameof(ConfirmPassword) });
}
}
// 这里可以添加更多依赖多个属性的校验逻辑
// 比如校验手机号是否已经被注册,需要调用服务层方法
}
}
两种方式的对比与结合使用
两种方式各有适用场景,我们可以通过下面的表格来对比:
| 对比项 | DataAnnotations自定义特性 | IValidatableObject接口 |
|---|---|---|
| 适用场景 | 单个属性的简单规则校验,可复用的通用规则 | 多属性关联校验,复杂业务逻辑校验 |
| 复用性 | 高,特性可以在多个模型的多个属性上重复使用 | 低,逻辑写在当前模型内部,仅当前模型可用 |
| 实现复杂度 | 需要单独定义特性类,相对繁琐 | 直接在模型内实现方法,相对简单 |
在实际开发中,我们通常将两种方式结合使用:通用的单个属性规则用DataAnnotations自定义特性实现,多属性关联的复杂规则用IValidatableObject实现,这样既能保证规则的复用性,又能满足复杂业务的需求。
验证触发与结果获取
在ASP.NET Core中,模型验证会自动触发,我们可以在控制器中获取验证结果:
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/user")]
public class UserController : ControllerBase
{
[HttpPost("register")]
public IActionResult Register(UserRegisterModel model)
{
// 检查模型状态是否有效,自动触发DataAnnotations和IValidatableObject验证
if (!ModelState.IsValid)
{
// 返回验证错误信息
return BadRequest(ModelState);
}
// 验证通过,执行业务逻辑
return Ok("注册成功");
}
}
如果是控制台程序或者业务层使用,也可以手动触发验证:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class ValidationHelper
{
public static List<string> ValidateModel(object model)
{
var results = new List<ValidationResult>();
var context = new ValidationContext(model);
// 手动触发验证,包含DataAnnotations特性和IValidatableObject接口的逻辑
Validator.TryValidateObject(model, context, results, true);
var errorMessages = new List<string>();
foreach (var result in results)
{
errorMessages.Add(result.ErrorMessage);
}
return errorMessages;
}
}
C#DataAnnotationsIValidatableObject模型验证修改时间:2026-06-13 02:15:38