Skip to content

Commit

Permalink
Resolve issue with unexpected results when with nested Include calls …
Browse files Browse the repository at this point in the history
…with the MemberNameValidatorSelector (#1989)
  • Loading branch information
JeremySkinner committed Aug 8, 2022
1 parent 6be268a commit 41ca856
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 4 deletions.
3 changes: 3 additions & 0 deletions Changelog.txt
@@ -1,3 +1,6 @@
11.2.0 - 8 August 2022
Resolve issue with unexpected results when with nested Include calls with the MemberNameValidatorSelector (#1989)

11.1.1 - 6 August 2022
Fix issue with incorrect rulesets being executed in a child validator when combined with ChildRules (#1981)
Bulgarian translations of default messages (#1973)
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<VersionPrefix>11.1.1</VersionPrefix>
<VersionPrefix>11.2.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
<!-- Use CI build number as version suffix (if defined) -->
<!--<VersionSuffix Condition="'$(GITHUB_RUN_NUMBER)'!=''">ci-$(GITHUB_RUN_NUMBER)</VersionSuffix>-->
Expand Down
24 changes: 23 additions & 1 deletion src/FluentValidation.Tests/ValidatorSelectorTests.cs
Expand Up @@ -21,7 +21,6 @@ namespace FluentValidation.Tests;
using Xunit;
using System.Threading.Tasks;


public class ValidatorSelectorTests {

[Fact]
Expand Down Expand Up @@ -141,6 +140,29 @@ public class ValidatorSelectorTests {
result.Errors[0].PropertyName.ShouldEqual("Forename");
}

[Fact]
public void Executes_correct_rule_when_using_property_with_nested_includes() {
var validator3 = new TestValidator();
validator3.RuleFor(x => x.Age).GreaterThan(0);

// In the middle validator ensure that the Include statement is
// before the additional rules in order to trigger the case reported in
// https://github.com/FluentValidation/FluentValidation/issues/1989
var validator2 = new TestValidator();
validator2.Include(validator3);
validator2.RuleFor(x => x.Orders).NotEmpty();

var validator = new TestValidator();
validator.Include(validator2);

var result = validator.Validate(new Person(), v => v.IncludeProperties("Age"));
result.Errors.Count.ShouldEqual(1);
result.Errors[0].PropertyName.ShouldEqual("Age");

result = validator.Validate(new Person { Age = 1 }, v => v.IncludeProperties("Age"));
result.Errors.Count.ShouldEqual(0);
}

private class TestObject {
public object SomeProperty { get; set; }
public object SomeOtherProperty { get; set; }
Expand Down
19 changes: 17 additions & 2 deletions src/FluentValidation/Internal/IncludeRule.cs
Expand Up @@ -53,8 +53,23 @@ public static IncludeRule<T> Create<TValidator>(Func<T, TValidator> func, Func<C
}

public override async ValueTask ValidateAsync(ValidationContext<T> context, bool useAsync, CancellationToken cancellation) {
context.RootContextData[MemberNameValidatorSelector.DisableCascadeKey] = true;
// Special handling for the MemberName selector.
// We need to disable the MemberName selector's cascade functionality whilst executing
// an include rule, as an include rule should be have as if its children are actually children of the parent.
// Also ensure that we only add/remove the state key if it's not present already.
// If it is present already then we're in a situation where there are nested Include rules
// in which case only the root Include rule should add/remove the key.
// See https://github.com/FluentValidation/FluentValidation/issues/1989
bool shouldAddStateKey = !context.RootContextData.ContainsKey(MemberNameValidatorSelector.DisableCascadeKey);

if (shouldAddStateKey) {
context.RootContextData[MemberNameValidatorSelector.DisableCascadeKey] = true;
}

await base.ValidateAsync(context, useAsync, cancellation);
context.RootContextData.Remove(MemberNameValidatorSelector.DisableCascadeKey);

if (shouldAddStateKey) {
context.RootContextData.Remove(MemberNameValidatorSelector.DisableCascadeKey);
}
}
}

0 comments on commit 41ca856

Please sign in to comment.