如何在Fluent Validation中通过自定义规则校验其他属性
问题描述
假设存在如下类:
public class MyClass { public string Property1 { get; set; } public string Property2 {get; set; } public string Property3 { get; set; } }
以及对应的验证器类:
public class MyClassValidator : AbstractValidator<MyClass> { public MyClassValidator () { RuleFor(myClass => myClass.Property1).Property1(); } }
当前已有Property1的自定义验证规则:
public static IRuleBuilderOptions<T, string> Property1<T>(this IRuleBuilder<T, string> ruleBuilder) { return ruleBuilder.NotNull() .NotEmpty() .Length(12); }
现希望修改该自定义规则,实现当Property2的值为"qwerty"时,校验Property1的值不能为"q",但原写法无法正确获取并使用Property2的值,同时希望该校验规则能复用在其他类中。
解决方案
1. 核心思路:用接口约束保证复用性,通过上下文获取父对象
要在Property1的校验规则里拿到Property2的值,得借助FluentValidation的Custom方法获取父对象上下文,同时通过泛型接口约束,确保只要类有Property2就能复用这个规则,避免写死依赖特定类。
2. 修改后的通用自定义规则
先定义一个接口,约束类必须包含Property2:
public interface IHasProperty2 { string Property2 { get; set; } }
然后修改自定义扩展方法,加上泛型约束,同时新增条件校验逻辑:
public static IRuleBuilderOptions<T, string> Property1<T>(this IRuleBuilder<T, string> ruleBuilder) where T : IHasProperty2 { return ruleBuilder // 保留原有校验规则 .NotNull() .NotEmpty() .Length(12) // 添加自定义条件校验 .Custom((property1Value, context) => { // 从上下文拿到当前校验的父对象实例 var targetObj = (T)context.InstanceToValidate; // 判断触发条件:Property2是"qwerty"且Property1是"q"时,添加失败提示 if (targetObj.Property2 == "qwerty" && property1Value == "q") { context.AddFailure("当Property2为qwerty时,Property1不能为q"); } }); }
3. 让目标类实现接口即可复用规则
不管是原来的MyClass还是其他需要用这个规则的类,只要实现IHasProperty2接口就行:
// 原MyClass实现接口 public class MyClass : IHasProperty2 { public string Property1 { get; set; } public string Property2 {get; set; } public string Property3 { get; set; } } // 其他类复用示例 public class AnotherClass : IHasProperty2 { public string Property1 { get; set; } public string Property2 { get; set; } // 其他自定义属性... }
4. 验证器使用方式完全不变
不管哪个类的验证器,调用规则的方式和之前一样:
public class MyClassValidator : AbstractValidator<MyClass> { public MyClassValidator() { RuleFor(x => x.Property1).Property1(); } } public class AnotherClassValidator : AbstractValidator<AnotherClass> { public AnotherClassValidator() { RuleFor(x => x.Property1).Property1(); } }
要点总结
- 泛型约束
where T : IHasProperty2保证了只有符合条件的类能使用规则,避免编译报错。 context.InstanceToValidate是获取父对象的关键,通过它能拿到当前校验对象的所有属性。- 原有校验逻辑完全保留,只新增了条件校验,实现了增量修改。
内容的提问来源于stack exchange,提问作者Aleksey K
相关产品推荐
相关产品推荐

