FluentValidation中.Empty()与.When()组合的验证规则未按预期生效
FluentValidation中.Empty()与.When()组合的验证规则未按预期生效
我仔细看了你的代码和问题描述,核心问题是你把验证规则的逻辑完全搞反了!先明确你的核心需求:模型必须满足二选一的要求——要么提供SelectedSourceSetId(非空)且ProvidedEans为空/空集合,要么提供ProvidedEans(至少一个元素)且SelectedSourceSetId为空。同时要排除两种非法情况:两者都未提供,或者两者都提供。
你的现有规则存在的问题
- 第一个规则逻辑完全反转:你当前的规则是「当
ProvidedEans为空时,要求SelectedSourceSetId必须Empty」,但实际你需要的是「当ProvidedEans为空时,SelectedSourceSetId必须非空」,否则触发“必须二选一”的错误。 - 第二个规则逻辑也反转了:你当前的规则是「当
ProvidedEans有内容时,要求SelectedSourceSetId必须非空」,但实际你需要的是「当ProvidedEans有内容时,SelectedSourceSetId必须为空」,否则触发“不能同时选两个”的错误。 - 测试代码还有笔误:你的
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
相关产品推荐
相关产品推荐

