From 6bdf76cf1ab68e956b0dcc4d74682fe4aa6feaa2 Mon Sep 17 00:00:00 2001 From: Jeremy Skinner <90130+JeremySkinner@users.noreply.github.com> Date: Thu, 8 Sep 2022 09:43:02 +0100 Subject: [PATCH] Ensure disabling of implicit child validation works correctly for record types (#7) (#8) --- .../FluentValidation.AspNetCore.csproj | 2 +- .../FluentValidationModelValidatorProvider.cs | 6 +++++ .../Controllers/TestController.cs | 5 +++++ .../ImplicitValidationTests.cs | 22 +++++++++++++++++++ .../TestModels.cs | 18 +++++++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/FluentValidation.AspNetCore/FluentValidation.AspNetCore.csproj b/src/FluentValidation.AspNetCore/FluentValidation.AspNetCore.csproj index 792d517..5403b6d 100644 --- a/src/FluentValidation.AspNetCore/FluentValidation.AspNetCore.csproj +++ b/src/FluentValidation.AspNetCore/FluentValidation.AspNetCore.csproj @@ -1,7 +1,7 @@  netcoreapp3.1;net5.0;net6.0 - 11.2.1 + 11.2.2 FluentValidation.AspNetCore FluentValidation.AspNetCore FluentValidation.AspNetCore diff --git a/src/FluentValidation.AspNetCore/FluentValidationModelValidatorProvider.cs b/src/FluentValidation.AspNetCore/FluentValidationModelValidatorProvider.cs index 0803bab..0d7293d 100644 --- a/src/FluentValidation.AspNetCore/FluentValidationModelValidatorProvider.cs +++ b/src/FluentValidation.AspNetCore/FluentValidationModelValidatorProvider.cs @@ -187,6 +187,12 @@ protected bool ShouldSkip(ModelValidationContext mvContext) { return true; } } + else if (modelMetadata.MetadataKind == ModelMetadataKind.Parameter) { + // If we're working with record types then metadata kind will always be parameter. + if (!ReferenceEquals(rootMetadata, modelMetadata)) { + return true; + } + } } return false; diff --git a/src/FluentValidation.Tests.AspNetCore/Controllers/TestController.cs b/src/FluentValidation.Tests.AspNetCore/Controllers/TestController.cs index ee428bf..f8dfbd6 100644 --- a/src/FluentValidation.Tests.AspNetCore/Controllers/TestController.cs +++ b/src/FluentValidation.Tests.AspNetCore/Controllers/TestController.cs @@ -156,6 +156,11 @@ public ActionResult ImplicitChildCollectionDataAnnotations([FromBody] ParentMode return TestResult(); } +#if !NETCOREAPP3_1 + public ActionResult ImplicitChildWithRecord([FromBody] ParentRecord model) { + return TestResult(); + } +#endif public ActionResult CheckUnvalidated([FromBody] ParentModel6 model) { return Content(ModelState.Count(x => x.Value.ValidationState == ModelValidationState.Unvalidated).ToString()); diff --git a/src/FluentValidation.Tests.AspNetCore/ImplicitValidationTests.cs b/src/FluentValidation.Tests.AspNetCore/ImplicitValidationTests.cs index 4bad0f5..6d715d1 100644 --- a/src/FluentValidation.Tests.AspNetCore/ImplicitValidationTests.cs +++ b/src/FluentValidation.Tests.AspNetCore/ImplicitValidationTests.cs @@ -39,6 +39,10 @@ private HttpClient CreateClient(bool implicitValidationEnabled) { services.AddScoped, CollectionTestModelValidator>(); services.AddScoped, TestModelValidator>(); services.AddScoped, TestModel5Validator>(); +#if !NETCOREAPP3_1 + services.AddScoped, ParentRecordValidator>(); + services.AddScoped, ChildRecordValidator>(); +#endif }); } @@ -192,5 +196,23 @@ public async void Skips_implicit_child_validation() { var result = await CreateClient(true).GetErrors("SkipsImplicitChildValidator", new FormData()); result.Count.ShouldEqual(0); } +#if !NETCOREAPP3_1 + [Fact] + public async void Does_not_run_child_validator_when_implicit_child_validation_disabled_for_record() { + var json = @"{""Name"": ""Foo"", ""Child"": { ""Count"": 0 } }"; + var client = CreateClient(false); + var result = await client.GetErrorsViaJSONRaw("ImplicitChildWithRecord", json); + result.Count.ShouldEqual(0); + } + [Fact] + public async void Runs_child_validator_when_implicit_child_validation_enabled_for_record() { + var json = @"{""Name"": ""Foo"", ""Child"": { ""Count"": 0 } }"; + + var client = CreateClient(true); + var result = await client.GetErrorsViaJSONRaw("ImplicitChildWithRecord", json); + result.Count.ShouldEqual(1); + result[0].Name.ShouldEqual("Child.Count"); + } +#endif } diff --git a/src/FluentValidation.Tests.AspNetCore/TestModels.cs b/src/FluentValidation.Tests.AspNetCore/TestModels.cs index b974666..484f79f 100644 --- a/src/FluentValidation.Tests.AspNetCore/TestModels.cs +++ b/src/FluentValidation.Tests.AspNetCore/TestModels.cs @@ -459,3 +459,21 @@ public BadAsyncValidator() { }); } } + +#if !NETCOREAPP3_1 +public sealed record ParentRecord(string Name, ChildRecord Child); + +public record ChildRecord(int Count); + +public class ParentRecordValidator : AbstractValidator { + public ParentRecordValidator() { + RuleFor(x => x.Name).NotNull(); + } +} + +public class ChildRecordValidator : AbstractValidator { + public ChildRecordValidator() { + RuleFor(x => x.Count).GreaterThan(0); + } +} +#endif