From a437761052237fdf1102b55014ba6ed1da0f2411 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner <90130+JeremySkinner@users.noreply.github.com> Date: Wed, 11 Oct 2023 22:01:06 +0100 Subject: [PATCH] Fix ChildRules with hierarchies #2165 --- src/FluentValidation.Tests/ChildRulesTests.cs | 39 +++++++++++++++++++ .../DefaultValidatorExtensions.cs | 2 +- src/FluentValidation/Syntax.cs | 6 ++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/FluentValidation.Tests/ChildRulesTests.cs b/src/FluentValidation.Tests/ChildRulesTests.cs index d1f2c48c1..625df6adc 100644 --- a/src/FluentValidation.Tests/ChildRulesTests.cs +++ b/src/FluentValidation.Tests/ChildRulesTests.cs @@ -20,6 +20,7 @@ namespace FluentValidation.Tests; +using System; using System.Collections.Generic; using Xunit; @@ -121,6 +122,14 @@ public void Multiple_levels_of_nested_child_rules_in_ruleset() { result.IsValid.ShouldBeFalse(); } + [Fact] + public void Doesnt_throw_InvalidCastException() { + // See https://github.com/FluentValidation/FluentValidation/issues/2165 + var validator = new RootValidator(); + var result = validator.Validate(new Root { Data = new() { Value = -1 }}); + result.Errors.Count.ShouldEqual(1); + } + private class RulesetChildRulesValidator : AbstractValidator { public RulesetChildRulesValidator() { RuleSet("testing", () => { @@ -163,4 +172,34 @@ public class Baz { } } + public class Root { + public Bar Data {get; set;} = null; + } + + public class Base { + public int Value {get; set;} + } + + public class Bar : Base { + public int BarValue {get; set;} + } + + public class RootValidator : AbstractValidator { + public RootValidator() { + RuleFor(x => x).ChildRules(RootRules()); + } + + public static Action> BaseRules() { + return rules => { + rules.RuleFor(x => x.Value).NotEqual(-1); + }; + } + + public static Action> RootRules() { + return rules => { + rules.RuleFor(x => x.Data).ChildRules(BaseRules()); + }; + } + } + } diff --git a/src/FluentValidation/DefaultValidatorExtensions.cs b/src/FluentValidation/DefaultValidatorExtensions.cs index 05786a544..7ad162d2f 100644 --- a/src/FluentValidation/DefaultValidatorExtensions.cs +++ b/src/FluentValidation/DefaultValidatorExtensions.cs @@ -1204,7 +1204,7 @@ public static IRuleBuilderOptions IsEnumName(this IRuleBuilder ChildRules(this IRuleBuilder ruleBuilder, Action> action) { if (action == null) throw new ArgumentNullException(nameof(action)); var validator = new ChildRulesContainer(); - var parentValidator = ((RuleBuilder) ruleBuilder).ParentValidator; + var parentValidator = ((IRuleBuilderInternal) ruleBuilder).ParentValidator; string[] ruleSets; diff --git a/src/FluentValidation/Syntax.cs b/src/FluentValidation/Syntax.cs index dd4c18373..db6b4ac87 100644 --- a/src/FluentValidation/Syntax.cs +++ b/src/FluentValidation/Syntax.cs @@ -113,6 +113,10 @@ public interface IConditionBuilder { void Otherwise(Action action); } -internal interface IRuleBuilderInternal { +internal interface IRuleBuilderInternal : IRuleBuilderInternal { IValidationRule Rule { get; } } + +internal interface IRuleBuilderInternal { + AbstractValidator ParentValidator { get; } +}