You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 09:03:09