FluentValidation中类Switch条件验证替代方案及状态添加问题
FluentValidation 实现多分支条件验证(替代嵌套When/Otherwise)
问题场景
处理含50+列的CSV转换对象,通过鉴别列discriminator1的不同取值,对对应字段执行不同的非空验证规则:
- 每个不符合规则的字段需生成独立错误提示
- 默认分支需针对未知鉴别值抛出错误
- 需通过
WithState标记受影响的行号(处理数千条数据) - 嵌套使用
When/Otherwise会产生10+层级,难以维护;自定义Custom方法无法直接用WithState绑定状态
解决方案
方案1:独立规则+When条件(推荐,符合FluentValidation设计风格)
放弃嵌套When/Otherwise,为每个鉴别值单独编写带条件的RuleFor,每个规则支持WithState绑定行号,同时保证错误独立。
public class YourCsvModelValidator : AbstractValidator<YourCsvModel> { public YourCsvModelValidator() { // 第一步:验证鉴别值合法性 RuleFor(x => x.discriminator1) .Must(BeValidDiscriminator) .WithMessage("未知鉴别值: {PropertyValue}") .WithState(x => x.RowNumber); // TypeA专属字段验证 RuleFor(x => x.TypeA_Field1) .NotEmpty() .WithMessage("TypeA字段Field1不能为空") .WithState(x => x.RowNumber) .When(x => x.discriminator1 == "typeA"); RuleFor(x => x.TypeA_Field2) .NotEmpty() .WithMessage("TypeA字段Field2不能为空") .WithState(x => x.RowNumber) .When(x => x.discriminator1 == "typeA"); // TypeB专属字段验证 RuleFor(x => x.TypeB_FieldX) .NotEmpty() .WithMessage("TypeB字段FieldX不能为空") .WithState(x => x.RowNumber) .When(x => x.discriminator1 == "typeB"); // 其他鉴别值规则以此类推... } // 封装鉴别值合法性判断 private bool BeValidDiscriminator(string value) { return new[] { "typeA", "typeB", /* 所有合法鉴别值 */ }.Contains(value); } }
方案2:封装规则集合(减少重复代码)
如果鉴别值类型较多,可以将同类型的规则封装成独立方法,提升代码可维护性:
public class YourCsvModelValidator : AbstractValidator<YourCsvModel> { public YourCsvModelValidator() { // 验证鉴别值合法性 RuleFor(x => x.discriminator1) .Must(BeValidDiscriminator) .WithMessage("未知鉴别值: {PropertyValue}") .WithState(x => x.RowNumber); // 批量添加各类型规则 AddTypeARules(); AddTypeBRules(); // AddTypeCRules(); ... } private void AddTypeARules() { RuleFor(x => x.TypeA_Field1) .NotEmpty() .WithMessage("TypeA字段Field1不能为空") .WithState(x => x.RowNumber) .When(x => x.discriminator1 == "typeA"); RuleFor(x => x.TypeA_Field2) .NotEmpty() .WithMessage("TypeA字段Field2不能为空") .WithState(x => x.RowNumber) .When(x => x.discriminator1 == "typeA"); } private void AddTypeBRules() { RuleFor(x => x.TypeB_FieldX) .NotEmpty() .WithMessage("TypeB字段FieldX不能为空") .WithState(x => x.RowNumber) .When(x => x.discriminator1 == "typeB"); } private bool BeValidDiscriminator(string value) { return new[] { "typeA", "typeB" }.Contains(value); } }
方案3:Custom方法+Switch(适合复杂逻辑场景)
如果必须用Switch集中管理分支逻辑,可直接在Custom中调用带State参数的AddFailure重载,手动绑定行号状态:
RuleFor(x => x) .Custom((model, context) => { switch (model.discriminator1) { case "typeA": // 验证TypeA字段 if (string.IsNullOrEmpty(model.TypeA_Field1)) { context.AddFailure( nameof(model.TypeA_Field1), "TypeA字段Field1不能为空", model.RowNumber // 绑定行号状态 ); } if (string.IsNullOrEmpty(model.TypeA_Field2)) { context.AddFailure( nameof(model.TypeA_Field2), "TypeA字段Field2不能为空", model.RowNumber ); } break; case "typeB": // 验证TypeB字段 if (string.IsNullOrEmpty(model.TypeB_FieldX)) { context.AddFailure( nameof(model.TypeB_FieldX), "TypeB字段FieldX不能为空", model.RowNumber ); } break; default: // 未知鉴别值错误 context.AddFailure( nameof(model.discriminator1), $"未知鉴别值: {model.discriminator1}", model.RowNumber ); break; } });
内容的提问来源于stack exchange,提问作者me.at.coding
相关产品推荐
相关产品推荐

