C#中如何向自定义验证器传递字段或动态变化的值
问题描述
初始定义的File类代码如下:
public class File { [Required, MinLength(1, ErrorMessage = "The Header should contain at least one element")] public List<string> Header { get; set; } [ColumnLenghts] public List<int> ColumnLenghts { get; set; } //other fields }
需要在自定义的ColumnLenghts验证器中实现校验逻辑:判断ColumnLenghts的元素数量与Header的元素数量一致,验证器初始代码如下:
public class ColumnLenghtsAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { List<int> columnLengths = (List<int>) value; // other checks if(columnLengths.Count != Header.Count) { return new ValidationResult(" number of column length does not match to headers"); } return ValidationResult.Success; } }
尝试通过给特性传headerCount = Header.Count的方式获取Header长度时,编译报错:an object reference is required for the non-static field,需要确认如何在自定义验证器中获取类实例的动态字段/属性值。
报错原因
C#中特性的参数属于编译期常量,在代码编译阶段就会被解析固化,而类的实例字段/属性是运行时创建对象后才会生成具体值,编译阶段不存在实例对象,自然无法直接引用非静态的Header.Count给特性属性赋值,这就是报错的根本原因。
实现方案
不需要在特性标注时传递动态值,自定义验证方法入参里的ValidationContext对象已经携带了当前被校验的类实例信息,直接通过它就能访问同实例下的其他属性,这是实现跨属性校验的标准方式。
针对当前场景的简单实现
直接在验证逻辑中获取当前File实例,访问Header属性做对比即可:
public class ColumnLenghtsAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // 获取当前被校验的File类实例 var file = validationContext.ObjectInstance as File; if (file == null) { return new ValidationResult("校验目标类型错误"); } List<int> columnLengths = value as List<int>; // 空值场景交给Required等独立校验特性处理,此处直接放行 if (columnLengths == null || file.Header == null) { return ValidationResult.Success; } if(columnLengths.Count != file.Header.Count) { return new ValidationResult("列长度配置的数量与表头列数不匹配"); } return ValidationResult.Success; } }
使用时不需要给ColumnLenghts特性传任何额外参数,直接标注在属性上即可正常运行。
通用可复用实现
如果需要让这个验证特性不与File类强绑定,可以通过传入对比属性名、反射取值的方式做通用实现:
public class ColumnLenghtsAttribute : ValidationAttribute { /// <summary> /// 要对比元素数量的属性名 /// </summary> public string CompareProperty { get; set; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (string.IsNullOrWhiteSpace(CompareProperty)) { return new ValidationResult("未指定需要对比的属性"); } // 从当前校验实例中反射获取目标属性 var targetPropertyInfo = validationContext.ObjectType.GetProperty(CompareProperty); if (targetPropertyInfo == null) { return new ValidationResult($"未找到名为{CompareProperty}的属性"); } var targetValue = targetPropertyInfo.GetValue(validationContext.ObjectInstance) as IList; if (value is not IList currentList || targetValue == null) { // 空值场景交给其他校验特性处理 return ValidationResult.Success; } if (currentList.Count != targetValue.Count) { return new ValidationResult("当前集合元素数量与对比属性的集合元素数量不匹配"); } return ValidationResult.Success; } }
使用时只需要传入要对比的属性名即可,支持任意类型的集合长度对比:
public class File { [Required, MinLength(1, ErrorMessage = "The Header should contain at least one element")] public List<string> Header { get; set; } [ColumnLenghts(CompareProperty = nameof(Header))] public List<int> ColumnLenghts { get; set; } //other fields }
内容的提问来源于stack exchange,提问作者Elnur
相关产品推荐
相关产品推荐

