From 83b4d6d46ee3cc1624351c2c74602f1c08b428a1 Mon Sep 17 00:00:00 2001 From: Frank Zheng Date: Mon, 1 Aug 2022 17:46:46 +1000 Subject: [PATCH 1/3] Fix the issue that the ChildRules will corrupt the RuleSets of a validator passed to the SetValidator --- src/FluentValidation.Tests/ChildRulesTests.cs | 55 ++++++++++++++++++- .../DefaultValidatorExtensions.cs | 8 ++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/FluentValidation.Tests/ChildRulesTests.cs b/src/FluentValidation.Tests/ChildRulesTests.cs index 36308c71a..d4fc187bd 100644 --- a/src/FluentValidation.Tests/ChildRulesTests.cs +++ b/src/FluentValidation.Tests/ChildRulesTests.cs @@ -33,11 +33,13 @@ public class ChildRulesTests { order.RuleFor(x => x.Amount).GreaterThan(0); }); - var result = validator.Validate(new Person {Orders = new List { + var result = validator.Validate(new Person { + Orders = new List { new Order { ProductName = null, Amount = 10 }, new Order { ProductName = "foo", Amount = 0}, new Order { ProductName = "foo", Amount = 10 } - }}); + } + }); result.Errors.Count.ShouldEqual(2); result.Errors[0].PropertyName.ShouldEqual("Orders[0].ProductName"); @@ -71,7 +73,33 @@ public class ChildRulesTests { result.Errors.Count.ShouldEqual(0); } - private class RulesetChildRulesValidator : AbstractValidator { + [Fact] + public void ChildRules_works_with_SetValidator_and_RuleSet() { + var validator = new RulesetChildValidatorRulesValidator(); + + // If the validator inside a child rule specifies a rule set "b", + // the rules inside the rule set "b" should not be used for the validation + // if the validation context specified the ruleset "a" + var result = validator.Validate(new Person { + Orders = new List { + new Order() + } + }, options => options.IncludeRuleSets("a")); + + result.Errors.Count.ShouldEqual(1); + result.Errors[0].PropertyName.ShouldEqual("Surname"); + + // They shouldn't be executed if a different ruleset is chosen. + result = validator.Validate(new Person { + Orders = new List { + new Order() + } + }, options => options.IncludeRuleSets("other")); + + result.Errors.Count.ShouldEqual(0); + } + + private class RulesetChildRulesValidator : AbstractValidator { public RulesetChildRulesValidator() { RuleSet("testing", () => { RuleFor(a => a.Surname).NotEmpty(); @@ -81,5 +109,26 @@ private class RulesetChildRulesValidator : AbstractValidator { }); } } + + private class RulesetChildValidatorRulesValidator : AbstractValidator { + public RulesetChildValidatorRulesValidator() { + RuleSet("a, b", () => { + RuleFor(x => x.Surname).NotEmpty(); + RuleFor(x => x).ChildRules(child => { + child.RuleForEach(o => o.Orders).SetValidator(new RulesetOrderValidator()); + }); + }); + } + + private class RulesetOrderValidator : AbstractValidator { + public RulesetOrderValidator() { + RuleSet("b", () => { + RuleFor(o => o.ProductName).NotEmpty(); + }); + } + } + } + } } + diff --git a/src/FluentValidation/DefaultValidatorExtensions.cs b/src/FluentValidation/DefaultValidatorExtensions.cs index 3096ab40c..e60be4688 100644 --- a/src/FluentValidation/DefaultValidatorExtensions.cs +++ b/src/FluentValidation/DefaultValidatorExtensions.cs @@ -1169,8 +1169,14 @@ public static partial class DefaultValidatorExtensions { public static IRuleBuilderOptions ChildRules(this IRuleBuilder ruleBuilder, Action> action) { if (action == null) throw new ArgumentNullException(nameof(action)); var validator = new InlineValidator(); + var ruleSets = ((IRuleBuilderInternal)ruleBuilder).Rule.RuleSets; action(validator); - return ruleBuilder.SetValidator(validator, "*"); + foreach(var rule in validator.Rules) { + if (rule.RuleSets == null) { + rule.RuleSets = ruleSets; + } + } + return ruleBuilder.SetValidator(validator, ruleSets); } /// From 12aae6d3802489e579fd6f76c47b7341b74afc49 Mon Sep 17 00:00:00 2001 From: Frank Zheng Date: Fri, 5 Aug 2022 16:22:09 +1000 Subject: [PATCH 2/3] Fix the test case and a missing file. --- .../ChainedValidationTester.cs | 5 ++--- src/FluentValidation.Tests/ChildRulesTests.cs | 12 ++---------- src/FluentValidation/DefaultValidatorExtensions.cs | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/FluentValidation.Tests/ChainedValidationTester.cs b/src/FluentValidation.Tests/ChainedValidationTester.cs index 612f21e10..3796852a9 100644 --- a/src/FluentValidation.Tests/ChainedValidationTester.cs +++ b/src/FluentValidation.Tests/ChainedValidationTester.cs @@ -114,8 +114,7 @@ public class ChainedValidationTester { [Fact] public void Separate_validation_on_chained_property() { var validator = new DepartmentValidator(); - var result = validator.Validate(new Department - { + var result = validator.Validate(new Department { Manager = new Person(), Assistant = new Person() }); @@ -171,7 +170,7 @@ public class ChainedValidationTester { var validator = new InlineValidator(); validator.RuleFor(x => x.Address).SetValidator(addressValidator, ruleSets: "ruleset1"); - var result = validator.Validate(new Person {Address = new Address()}); + var result = validator.Validate(new Person { Address = new Address() }); result.Errors.Count.ShouldEqual(1); result.Errors[0].PropertyName.ShouldEqual("Address.Line1"); } diff --git a/src/FluentValidation.Tests/ChildRulesTests.cs b/src/FluentValidation.Tests/ChildRulesTests.cs index d4fc187bd..f6851eb38 100644 --- a/src/FluentValidation.Tests/ChildRulesTests.cs +++ b/src/FluentValidation.Tests/ChildRulesTests.cs @@ -19,6 +19,7 @@ #endregion namespace FluentValidation.Tests { + using FluentValidation.Results; using System.Collections.Generic; using Xunit; @@ -87,16 +88,7 @@ public class ChildRulesTests { }, options => options.IncludeRuleSets("a")); result.Errors.Count.ShouldEqual(1); - result.Errors[0].PropertyName.ShouldEqual("Surname"); - - // They shouldn't be executed if a different ruleset is chosen. - result = validator.Validate(new Person { - Orders = new List { - new Order() - } - }, options => options.IncludeRuleSets("other")); - - result.Errors.Count.ShouldEqual(0); + result.Errors[0].PropertyName.ShouldEqual("Surname"); } private class RulesetChildRulesValidator : AbstractValidator { diff --git a/src/FluentValidation/DefaultValidatorExtensions.cs b/src/FluentValidation/DefaultValidatorExtensions.cs index e60be4688..7ee210ba7 100644 --- a/src/FluentValidation/DefaultValidatorExtensions.cs +++ b/src/FluentValidation/DefaultValidatorExtensions.cs @@ -1176,7 +1176,7 @@ public static partial class DefaultValidatorExtensions { rule.RuleSets = ruleSets; } } - return ruleBuilder.SetValidator(validator, ruleSets); + return ruleBuilder.SetValidator(validator); } /// From 7f6a9017d9bf35b957505287985cbc99186f47d2 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner <90130+JeremySkinner@users.noreply.github.com> Date: Fri, 5 Aug 2022 10:17:47 +0100 Subject: [PATCH 3/3] Remove cast --- src/FluentValidation/DefaultValidatorExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FluentValidation/DefaultValidatorExtensions.cs b/src/FluentValidation/DefaultValidatorExtensions.cs index 7ee210ba7..26418a1da 100644 --- a/src/FluentValidation/DefaultValidatorExtensions.cs +++ b/src/FluentValidation/DefaultValidatorExtensions.cs @@ -1169,7 +1169,7 @@ public static partial class DefaultValidatorExtensions { public static IRuleBuilderOptions ChildRules(this IRuleBuilder ruleBuilder, Action> action) { if (action == null) throw new ArgumentNullException(nameof(action)); var validator = new InlineValidator(); - var ruleSets = ((IRuleBuilderInternal)ruleBuilder).Rule.RuleSets; + var ruleSets = DefaultValidatorOptions.Configurable(ruleBuilder).RuleSets; action(validator); foreach(var rule in validator.Rules) { if (rule.RuleSets == null) {