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

FluentValidation中.Empty()与.When()组合的验证规则未按预期生效

FluentValidation中.Empty()与.When()组合的验证规则未按预期生效

我仔细看了你的代码和问题描述,核心问题是你把验证规则的逻辑完全搞反了!先明确你的核心需求:模型必须满足二选一的要求——要么提供SelectedSourceSetId(非空)且ProvidedEans为空/空集合,要么提供ProvidedEans(至少一个元素)且SelectedSourceSetId为空。同时要排除两种非法情况:两者都未提供,或者两者都提供。

你的现有规则存在的问题

  1. 第一个规则逻辑完全反转:你当前的规则是「当ProvidedEans为空时,要求SelectedSourceSetId必须Empty」,但实际你需要的是「当ProvidedEans为空时,SelectedSourceSetId必须非空」,否则触发“必须二选一”的错误。
  2. 第二个规则逻辑也反转了:你当前的规则是「当ProvidedEans有内容时,要求SelectedSourceSetId必须非空」,但实际你需要的是「当ProvidedEans有内容时,SelectedSourceSetId必须为空」,否则触发“不能同时选两个”的错误。
  3. 测试代码还有笔误:你的Model类中并没有WorkId属性,测试代码里的WorkId = 1234,需要删除,否则编译不通过;另外SetName = "Set Name 1"后面少了逗号。

修正后的验证器代码

下面是贴合你需求的修正版ModelValidator,我保留了你原来的When风格写法,只调整了逻辑:

public class ModelValidator : AbstractValidator<Model>
{
    public ModelValidator()
    {
        // 保留SetName的非空验证
        RuleFor(model => model.SetName).NotEmpty();

        // 规则1:当ProvidedEans为空(null或空集合)时,SelectedSourceSetId必须非空
        // 非法场景:两者都未提供
        RuleFor(model => model.SelectedSourceSetId)
            .NotEmpty()
            .When(model => model.ProvidedEans == null || !model.ProvidedEans.Any())
            .WithMessage("Either an existing override/yaml set or a list of Eans must be provided.");

        // 规则2:当ProvidedEans有至少一个元素时,SelectedSourceSetId必须为空
        // 非法场景:两者都提供了
        RuleFor(model => model.SelectedSourceSetId)
            .Empty()
            .When(model => model.ProvidedEans?.Any() ?? false)
            .WithMessage("Choose either an existing override/yaml set or provide a list of Eans, not both.");
    }
}

修正后的测试代码(覆盖所有场景)

我给你写了覆盖所有合法/非法场景的测试代码,你可以直接运行验证:

static void Main(string[] args)
{
    var validator = new ModelValidator();

    // 场景1:只提供SelectedSourceSetId(合法,应该通过)
    var model1 = new Model
    {
        Ean = "9780316587266",
        SetName = "Set Name 1",
        SelectedSourceSetId = "a59ea6d32f6444c8afef284e557ab319"
    };
    var result1 = validator.Validate(model1);
    Console.WriteLine($"场景1(仅选已有集合):{(result1.IsValid ? "验证通过" : "验证失败")}");
    if (!result1.IsValid) PrintErrors(result1);

    // 场景2:只提供ProvidedEans(合法,应该通过)
    var model2 = new Model
    {
        Ean = "9780316587266",
        SetName = "Set Name 1",
        ProvidedEans = new List<string> { "9780316587266" }
    };
    var result2 = validator.Validate(model2);
    Console.WriteLine($"场景2(仅提供Ean列表):{(result2.IsValid ? "验证通过" : "验证失败")}");
    if (!result2.IsValid) PrintErrors(result2);

    // 场景3:同时提供两者(非法,应该失败)
    var model3 = new Model
    {
        Ean = "9780316587266",
        SetName = "Set Name 1",
        SelectedSourceSetId = "a59ea6d32f6444c8afef284e557ab319",
        ProvidedEans = new List<string> { "9780316587266" }
    };
    var result3 = validator.Validate(model3);
    Console.WriteLine($"场景3(同时提供两者):{(result3.IsValid ? "验证通过" : "验证失败")}");
    if (!result3.IsValid) PrintErrors(result3);

    // 场景4:两者都未提供(非法,应该失败)
    var model4 = new Model
    {
        Ean = "9780316587266",
        SetName = "Set Name 1"
    };
    var result4 = validator.Validate(model4);
    Console.WriteLine($"场景4:两者都未提供:{(result4.IsValid ? "验证通过" : "验证失败")}");
    if (!result4.IsValid) PrintErrors(result4);
}

// 辅助方法:打印错误信息
static void PrintErrors(ValidationResult result)
{
    foreach (var error in result.Errors)
    {
        Console.WriteLine($"- {error.PropertyName}: {error.ErrorMessage}");
    }
}

额外优化建议

如果觉得用When的写法不够直观,也可以用Must做跨字段验证,逻辑会更清晰:

public class ModelValidator : AbstractValidator<Model>
{
    public ModelValidator()
    {
        RuleFor(model => model.SetName).NotEmpty();

        // 验证:不能两者都未提供
        RuleFor(model => model)
            .Must(model => !(string.IsNullOrEmpty(model.SelectedSourceSetId) && (model.ProvidedEans == null || !model.ProvidedEans.Any())))
            .WithMessage("Either an existing override/yaml set or a list of Eans must be provided.");

        // 验证:不能两者都提供
        RuleFor(model => model)
            .Must(model => !(string.IsNullOrEmpty(model.SelectedSourceSetId) == false && (model.ProvidedEans?.Any() ?? false)))
            .WithMessage("Choose either an existing override/yaml set or provide a list of Eans, not both.");
    }
}

这种写法直接针对非法场景做判断,逻辑更直白,也更容易维护。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.09 03:04:36