如何让FluentValidation的规则依赖另外两条规则的校验结果?
FluentValidation 实现规则依赖校验(第三条规则仅在前两条全通过时执行)
问题描述
我希望在FluentValidation中实现第三条规则仅在前两条规则校验通过时才执行。示例代码如下:
RuleFor(e => e.FirstType) .Length(2, 10) .MustAsync(async (value, token) => await TypeIsValid(value, context)) .WithMessage("First Type is not valid."); RuleFor(e => e.SecondType) .Length(2, 10) .MustAsync(async (value, token) => await TypeIsValid(value, context)) .WithMessage("Second Type is not valid."); When(e => {/* 这里需要添加判断逻辑 */}, () => { RuleFor(e => e) .MustAsync(async (cw, token) => await TypesAreUnique(cw, context)) .WithMessage("Already exists"); }); RuleFor(e => e.Property3) .Length(2, 10); RuleFor(e => e.Property4) .Length(2, 10);
我认为When()并非合适的实现方式,只是用它来表达需求意图。我需要在{some condition here}处添加逻辑,判断前两条规则是否校验通过——若其中任意一条失败则不执行第三条规则。DependentRule无法满足需求,因为第三条规则依赖两条前置规则;CascadeMode.Stop也不适用,因为我需要运行所有其他规则。请问有什么解决方案?
解决方案:提取复用校验逻辑,在WhenAsync中组合判断
核心思路是把前两条规则中的校验逻辑(长度验证、TypeIsValid异步校验)提取为可复用的方法,既能在原规则中使用,也能在WhenAsync的条件里复用这些逻辑,确保只有当前两条规则的所有校验项都通过时,才触发第三条规则。
步骤1:提取可复用的校验方法
将长度校验和类型有效性校验封装为独立方法,避免代码重复:
// 校验字符串长度是否在2-10区间内 private bool IsLengthValid(string value) { return !string.IsNullOrEmpty(value) && value.Length is >= 2 and <= 10; } // 异步校验Type是否有效(复用原有逻辑) private async Task<bool> IsTypeValidAsync(string value, IValidationContext context, CancellationToken token) { return await TypeIsValid(value, context); }
步骤2:修改原有规则,复用提取的方法
更新FirstType和SecondType的规则,使用复用方法保持校验逻辑一致性:
RuleFor(e => e.FirstType) .Must(IsLengthValid) .WithMessage("First Type length must be between 2 and 10.") .MustAsync(async (value, token) => await IsTypeValidAsync(value, Context, token)) .WithMessage("First Type is not valid."); RuleFor(e => e.SecondType) .Must(IsLengthValid) .WithMessage("Second Type length must be between 2 and 10.") .MustAsync(async (value, token) => await IsTypeValidAsync(value, Context, token)) .WithMessage("Second Type is not valid.");
步骤3:在WhenAsync中组合判断前置规则结果
使用WhenAsync(支持异步条件),同时验证FirstType和SecondType的所有校验项是否通过:
WhenAsync(async (e, token) => { // 校验FirstType的长度和有效性 var firstTypeLengthOk = IsLengthValid(e.FirstType); var firstTypeValid = firstTypeLengthOk ? await IsTypeValidAsync(e.FirstType, Context, token) : false; // 校验SecondType的长度和有效性 var secondTypeLengthOk = IsLengthValid(e.SecondType); var secondTypeValid = secondTypeLengthOk ? await IsTypeValidAsync(e.SecondType, Context, token) : false; // 仅当前两条规则的所有校验都通过时,才执行第三条规则 return firstTypeLengthOk && firstTypeValid && secondTypeLengthOk && secondTypeValid; }, () => { RuleFor(e => e) .MustAsync(async (cw, token) => await TypesAreUnique(cw, Context)) .WithMessage("Already exists"); });
方案优势
- 原有的FirstType、SecondType规则正常执行,错误信息正常输出,不影响Property3、Property4等其他规则的运行。
- 第三条规则仅在前两条规则的全部校验项通过时触发,完全匹配需求。
- 复用校验逻辑避免了代码冗余,保证了规则判断的一致性。
备选方案:利用验证上下文手动校验(不推荐)
如果不想提取逻辑,也可以通过创建临时验证上下文手动执行前两条规则,但这种方式会重复执行校验逻辑,性能较差,且依赖FluentValidation内部实现,版本更新可能存在兼容性问题:
WhenAsync(async (e, token) => { // 创建临时上下文,单独校验FirstType和SecondType var firstTypeCtx = new ValidationContext<string>(e.FirstType); var secondTypeCtx = new ValidationContext<string>(e.SecondType); // 手动执行规则并获取结果 var firstTypeValid = await ValidateAsync(firstTypeCtx, token); var secondTypeValid = await ValidateAsync(secondTypeCtx, token); return firstTypeValid.IsValid && secondTypeValid.IsValid; }, () => { RuleFor(e => e) .MustAsync(async (cw, token) => await TypesAreUnique(cw, Context)) .WithMessage("Already exists"); });
内容的提问来源于stack exchange,提问作者JHJ
相关产品推荐
相关产品推荐

