From 30371b5718c58146609d0f520ebdb8c81fa27007 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Jul 2025 14:20:56 +0000 Subject: [PATCH 1/8] Initial plan From 4c1b13ea65ec36bed54cec2fd3486f445e1a18be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Jul 2025 14:34:16 +0000 Subject: [PATCH 2/8] Fix DAM attributes in ValidationAttributeCache to include PublicConstructors Co-authored-by: captainsafia <1857993+captainsafia@users.noreply.github.com> --- .../gen/Emitters/ValidationsGenerator.Emitter.cs | 8 ++++---- ...pesWithAttribute#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...dateComplexTypes#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...alidatableObject#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...ltipleNamespaces#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...lidateParameters#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...PolymorphicTypes#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...idateRecordTypes#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...pesWithAttribute#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...teRecursiveTypes#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...rsableProperties#ValidatableInfoResolver.g.verified.cs | 8 ++++---- ...itForExemptTypes#ValidatableInfoResolver.g.verified.cs | 8 ++++---- 12 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs b/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs index 8deb7be3a60e..370b48937c56 100644 --- a/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs +++ b/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs @@ -57,7 +57,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -67,7 +67,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -123,11 +123,11 @@ file static class GeneratedServiceCollectionExtensions {{GeneratedCodeAttribute}} file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs index 52220c7918fa..8d1e90a957e9 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -188,11 +188,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs index 6ed50bc4cf76..dcfb9562e0e9 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -194,11 +194,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs index 0e714f920f11..9b25b7bf6e28 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -145,11 +145,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs index 25df8f453930..a44d3f565f72 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -125,11 +125,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs index eb73f425fad1..b6fed758e501 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -143,11 +143,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs index a2520c96a2a2..f37634f9768e 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -175,11 +175,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs index a2ba7cbdc855..b59622211415 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -221,11 +221,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs index 52220c7918fa..8d1e90a957e9 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -188,11 +188,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs index f61ce950aac1..2d95a63f3199 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -116,11 +116,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs index 9641ecbc3032..89a27222c154 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -158,11 +158,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs index e4bf2083af57..f8158b6baef8 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Validation.Generated file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo { public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, global::System.Type propertyType, string name, @@ -39,7 +39,7 @@ public GeneratedValidatablePropertyInfo( Name = name; } - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] internal global::System.Type ContainingType { get; } internal string Name { get; } @@ -110,11 +110,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { From e052658341d2d9f8f6cb9b2cfe42925149ec24ad Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 10 Jul 2025 16:08:02 +0000 Subject: [PATCH 3/8] Add Trimming and NativeAoT tests for validation --- .../BasicMinimalApiWithValidation.cs | 166 ++++++++++++++++++ ....Extensions.Validation.NativeAotTests.proj | 9 + ...t.Extensions.Validation.TrimmingTests.proj | 9 + 3 files changed, 184 insertions(+) create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/BasicMinimalApiWithValidation.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/Microsoft.Extensions.Validation.NativeAotTests.proj create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.TrimmingTests/Microsoft.Extensions.Validation.TrimmingTests.proj diff --git a/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/BasicMinimalApiWithValidation.cs b/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/BasicMinimalApiWithValidation.cs new file mode 100644 index 000000000000..550bed789069 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/BasicMinimalApiWithValidation.cs @@ -0,0 +1,166 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Validation; + +var builder = WebApplication.CreateSlimBuilder(); + +builder.Services.AddValidation(); + +var app = builder.Build(); + +// Validation endpoints with different validatable types +app.MapGet("/customers/{id}", ([Range(1, int.MaxValue)] int id) => + $"Getting customer with ID: {id}"); + +app.MapPost("/customers", (Customer customer) => + TypedResults.Created($"/customers/{customer.Id}", customer)); + +app.MapPost("/orders", (Order order) => + TypedResults.Created($"/orders/{order.OrderId}", order)); + +app.MapPost("/products", (Product product) => + TypedResults.Ok(new ProductResponse("Product created", product))); + +app.MapPut("/addresses/{id}", (int id, Address address) => + TypedResults.Ok(new AddressResponse($"Address {id} updated", address))); + +app.MapPost("/users", (User user) => + TypedResults.Created($"/users/{user.Id}", user)); + +app.MapPost("/inventory", ([EvenNumber] int productId, [Required] string name) => + TypedResults.Ok(new InventoryResponse(productId, name))); + +// Endpoint with disabled validation +app.MapPost("/products/bulk", (Product[] products) => + TypedResults.Ok(new BulkProductResponse("Bulk products created", products.Length))) + .DisableValidation(); + +app.Run(); + +return 100; + +public class Customer +{ + [Required] + [Range(1, int.MaxValue)] + public int Id { get; set; } + + [Required] + [StringLength(100, MinimumLength = 2)] + public string Name { get; set; } = string.Empty; + + [EmailAddress] + public string Email { get; set; } = string.Empty; + + [Range(18, 120)] + [Display(Name = "Customer Age")] + public int Age { get; set; } + + // Complex property with nested validation + public Address Address { get; set; } = new(); +} + +public record Product( + [Required] string Name, + [Range(0.01, double.MaxValue)] decimal Price, + [StringLength(500)] string Description = "" +) +{ + [Required] + public string Category { get; set; } = string.Empty; +} + +public class Address +{ + [Required] + [StringLength(200)] + public string Street { get; set; } = string.Empty; + + [Required] + [StringLength(100)] + public string City { get; set; } = string.Empty; + + [Required] + [RegularExpression(@"^\d{5}(-\d{4})?$", ErrorMessage = "Invalid ZIP code format")] + public string ZipCode { get; set; } = string.Empty; + + [StringLength(2, MinimumLength = 2)] + public string State { get; set; } = string.Empty; +} + +public class Order : IValidatableObject +{ + [Required] + public string OrderId { get; set; } = string.Empty; + + [Required] + public int CustomerId { get; set; } + + [Range(0.01, double.MaxValue)] + public decimal Total { get; set; } + + public DateTime OrderDate { get; set; } = DateTime.Now; + + public IEnumerable Validate(ValidationContext validationContext) + { + if (OrderDate > DateTime.Now.AddDays(1)) + { + yield return new ValidationResult( + "Order date cannot be more than 1 day in the future.", + new[] { nameof(OrderDate) }); + } + + if (Total > 10000 && CustomerId == 0) + { + yield return new ValidationResult( + "High-value orders require a valid customer ID.", + new[] { nameof(CustomerId), nameof(Total) }); + } + } +} + +public record User( + [Required] [StringLength(50)] string Username, + [EmailAddress] string Email, + [Phone] string PhoneNumber = "" +) +{ + [Required] + [Range(1, int.MaxValue)] + public int Id { get; set; } + + [StringLength(100)] + public string DisplayName => $"{Username} ({Email})"; + + [CreditCard] + public string CreditCardNumber { get; set; } +} + +// Custom validation attribute +public class EvenNumberAttribute : ValidationAttribute +{ + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + if (value is int number && number % 2 != 0) + { + return new ValidationResult("The number must be even.", new[] { validationContext.MemberName }); + } + + return ValidationResult.Success; + } +} + +public record ProductResponse(string Message, Product Product); + +public record AddressResponse(string Message, Address Address); + +public record InventoryResponse(int ProductId, string Name); + +public record BulkProductResponse(string Message, int Count); \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/Microsoft.Extensions.Validation.NativeAotTests.proj b/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/Microsoft.Extensions.Validation.NativeAotTests.proj new file mode 100644 index 000000000000..8def5e6973a9 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/Microsoft.Extensions.Validation.NativeAotTests.proj @@ -0,0 +1,9 @@ + + + + + EnableRequestDelegateGenerator + + + + diff --git a/src/Validation/test/Microsoft.Extensions.Validation.TrimmingTests/Microsoft.Extensions.Validation.TrimmingTests.proj b/src/Validation/test/Microsoft.Extensions.Validation.TrimmingTests/Microsoft.Extensions.Validation.TrimmingTests.proj new file mode 100644 index 000000000000..8ab4ee50cbb1 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.TrimmingTests/Microsoft.Extensions.Validation.TrimmingTests.proj @@ -0,0 +1,9 @@ + + + + + EnableRequestDelegateGenerator + + + + From 125f729497fe376defd49660fdabddb96042e617 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 10 Jul 2025 19:50:29 +0000 Subject: [PATCH 4/8] Update emitted code and trim tests build --- eng/RequiresDelayedBuildProjects.props | 2 + .../SupportFiles/Directory.Build.targets | 2 + eng/testing/linker/project.csproj.template | 2 +- .../Emitters/ValidationsGenerator.Emitter.cs | 6 +- ...bute#ValidatableInfoResolver.g.received.cs | 242 +++++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 248 ++++++++++++++++ ...ject#ValidatableInfoResolver.g.received.cs | 199 +++++++++++++ ...aces#ValidatableInfoResolver.g.received.cs | 179 ++++++++++++ ...ters#ValidatableInfoResolver.g.received.cs | 197 +++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 229 +++++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 275 ++++++++++++++++++ ...bute#ValidatableInfoResolver.g.received.cs | 242 +++++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 170 +++++++++++ ...ties#ValidatableInfoResolver.g.received.cs | 212 ++++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 164 +++++++++++ 15 files changed, 2367 insertions(+), 2 deletions(-) create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs diff --git a/eng/RequiresDelayedBuildProjects.props b/eng/RequiresDelayedBuildProjects.props index 80b709a0986f..56f65de773a9 100644 --- a/eng/RequiresDelayedBuildProjects.props +++ b/eng/RequiresDelayedBuildProjects.props @@ -28,5 +28,7 @@ + + diff --git a/eng/testing/linker/SupportFiles/Directory.Build.targets b/eng/testing/linker/SupportFiles/Directory.Build.targets index 68afc5df3672..b05e614c036f 100644 --- a/eng/testing/linker/SupportFiles/Directory.Build.targets +++ b/eng/testing/linker/SupportFiles/Directory.Build.targets @@ -76,6 +76,8 @@ + + diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 160271a57fb7..38688d6a95f2 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -12,7 +12,7 @@ 99.9 <_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) - $(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated + $(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated;Microsoft.Extensions.Validation.Generated false {AdditionalProperties} diff --git a/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs b/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs index 370b48937c56..45b1426a1955 100644 --- a/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs +++ b/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs @@ -123,7 +123,11 @@ file static class GeneratedServiceCollectionExtensions {{GeneratedCodeAttribute}} file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..bba18fd6b92f --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,242 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithInheritance)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithInheritance), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithInheritance), + propertyType: typeof(string), + name: "EmailString", + displayName: "EmailString" + ), + ] + ); + return true; + } + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplayName", + displayName: "Valid identifier" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithMemberAttributes", + displayName: "PropertyWithMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithoutMemberAttributes", + displayName: "PropertyWithoutMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubTypeWithInheritance), + name: "PropertyWithInheritance", + displayName: "PropertyWithInheritance" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::System.Collections.Generic.List), + name: "ListOfSubTypes", + displayName: "ListOfSubTypes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithCustomValidationAttribute", + displayName: "IntegerWithCustomValidationAttribute" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "PropertyWithMultipleAttributes", + displayName: "PropertyWithMultipleAttributes" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..538c884403fe --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,248 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithInheritance)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithInheritance), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithInheritance), + propertyType: typeof(string), + name: "EmailString", + displayName: "EmailString" + ), + ] + ); + return true; + } + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplayName", + displayName: "Valid identifier" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithMemberAttributes", + displayName: "PropertyWithMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithoutMemberAttributes", + displayName: "PropertyWithoutMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubTypeWithInheritance), + name: "PropertyWithInheritance", + displayName: "PropertyWithInheritance" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::System.Collections.Generic.List), + name: "ListOfSubTypes", + displayName: "ListOfSubTypes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithDerivedValidationAttribute", + displayName: "IntegerWithDerivedValidationAttribute" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithCustomValidation", + displayName: "IntegerWithCustomValidation" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "PropertyWithMultipleAttributes", + displayName: "PropertyWithMultipleAttributes" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..34303dad9370 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,199 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::ValidatableSubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ValidatableSubType), + members: [] + ); + return true; + } + if (type == typeof(global::ComplexValidatableType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexValidatableType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexValidatableType), + propertyType: typeof(string), + name: "Value2", + displayName: "Value2" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexValidatableType), + propertyType: typeof(global::ValidatableSubType), + name: "SubType", + displayName: "SubType" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..b77ed0ac109d --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,179 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::NamespaceOne.Type)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::NamespaceOne.Type), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::NamespaceOne.Type), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::NamespaceTwo.Type)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::NamespaceTwo.Type), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::NamespaceTwo.Type), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..49e33a093432 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,197 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::TestService)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::TestService), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::TestService), + propertyType: typeof(int), + name: "Value", + displayName: "Value" + ), + ] + ); + return true; + } + if (type == typeof(global::System.Collections.Generic.Dictionary)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::System.Collections.Generic.Dictionary), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::System.Collections.Generic.Dictionary), + propertyType: typeof(global::System.Collections.Generic.ICollection), + name: "System.Collections.Generic.IDictionary.Values", + displayName: "System.Collections.Generic.IDictionary.Values" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::System.Collections.Generic.Dictionary), + propertyType: typeof(global::System.Collections.Generic.IEnumerable), + name: "System.Collections.Generic.IReadOnlyDictionary.Values", + displayName: "System.Collections.Generic.IReadOnlyDictionary.Values" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::System.Collections.Generic.Dictionary), + propertyType: typeof(global::TestService), + name: "this[]", + displayName: "this[]" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::System.Collections.Generic.Dictionary), + propertyType: typeof(global::System.Collections.ICollection), + name: "System.Collections.IDictionary.Values", + displayName: "System.Collections.IDictionary.Values" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..0d90e4a481aa --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,229 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::DerivedType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::DerivedType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::DerivedType), + propertyType: typeof(string), + name: "Value3", + displayName: "Value3" + ), + ] + ); + return true; + } + if (type == typeof(global::BaseType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::BaseType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::BaseType), + propertyType: typeof(int), + name: "Value1", + displayName: "Value 1" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::BaseType), + propertyType: typeof(string), + name: "Value2", + displayName: "Value2" + ), + ] + ); + return true; + } + if (type == typeof(global::DerivedValidatableType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::DerivedValidatableType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::DerivedValidatableType), + propertyType: typeof(string), + name: "Value3", + displayName: "Value3" + ), + ] + ); + return true; + } + if (type == typeof(global::BaseValidatableType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::BaseValidatableType), + members: [] + ); + return true; + } + if (type == typeof(global::ContainerType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ContainerType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ContainerType), + propertyType: typeof(global::BaseType), + name: "BaseType", + displayName: "BaseType" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ContainerType), + propertyType: typeof(global::BaseValidatableType), + name: "BaseValidatableType", + displayName: "BaseValidatableType" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..786e16b5bca4 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,275 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithInheritance)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithInheritance), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithInheritance), + propertyType: typeof(string), + name: "EmailString", + displayName: "EmailString" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithoutConstructor)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithoutConstructor), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithoutConstructor), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithoutConstructor), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::ValidatableRecord)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ValidatableRecord), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplayName", + displayName: "Valid identifier" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::SubType), + name: "PropertyWithMemberAttributes", + displayName: "PropertyWithMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::SubType), + name: "PropertyWithoutMemberAttributes", + displayName: "PropertyWithoutMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::SubTypeWithInheritance), + name: "PropertyWithInheritance", + displayName: "PropertyWithInheritance" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::SubTypeWithoutConstructor), + name: "PropertyOfSubtypeWithoutConstructor", + displayName: "PropertyOfSubtypeWithoutConstructor" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::System.Collections.Generic.List), + name: "ListOfSubTypes", + displayName: "ListOfSubTypes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithDerivedValidationAttribute", + displayName: "IntegerWithDerivedValidationAttribute" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithCustomValidation", + displayName: "IntegerWithCustomValidation" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "PropertyWithMultipleAttributes", + displayName: "PropertyWithMultipleAttributes" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..bba18fd6b92f --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,242 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithInheritance)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithInheritance), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithInheritance), + propertyType: typeof(string), + name: "EmailString", + displayName: "EmailString" + ), + ] + ); + return true; + } + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplayName", + displayName: "Valid identifier" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithMemberAttributes", + displayName: "PropertyWithMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithoutMemberAttributes", + displayName: "PropertyWithoutMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubTypeWithInheritance), + name: "PropertyWithInheritance", + displayName: "PropertyWithInheritance" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::System.Collections.Generic.List), + name: "ListOfSubTypes", + displayName: "ListOfSubTypes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithCustomValidationAttribute", + displayName: "IntegerWithCustomValidationAttribute" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "PropertyWithMultipleAttributes", + displayName: "PropertyWithMultipleAttributes" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..b267e4669007 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,170 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::RecursiveType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::RecursiveType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::RecursiveType), + propertyType: typeof(int), + name: "Value", + displayName: "Value" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::RecursiveType), + propertyType: typeof(global::RecursiveType), + name: "Next", + displayName: "Next" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..18c30835da75 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,212 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::ComplexTypeWithParsableProperties)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexTypeWithParsableProperties), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.Guid?), + name: "GuidWithRegularExpression", + displayName: "GuidWithRegularExpression" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.TimeOnly?), + name: "TimeOnlyWithRequiredValue", + displayName: "TimeOnlyWithRequiredValue" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(string), + name: "Url", + displayName: "Url" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.DateOnly?), + name: "DateOnlyWithRange", + displayName: "DateOnlyWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.DateTime?), + name: "DateTimeWithRange", + displayName: "DateTimeWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(decimal?), + name: "DecimalWithRange", + displayName: "DecimalWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.TimeSpan?), + name: "TimeSpanWithHourRange", + displayName: "TimeSpanWithHourRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(bool), + name: "BooleanWithRange", + displayName: "BooleanWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.Version), + name: "VersionWithRegex", + displayName: "VersionWithRegex" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..9db7cb497042 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,164 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file From 0d2bf5562298834a6c35cf7117e222441bbb2482 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 10 Jul 2025 20:25:37 +0000 Subject: [PATCH 5/8] Add more assertions to native AoT tests --- .../BasicMinimalApiWithValidation.cs | 91 ++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/BasicMinimalApiWithValidation.cs b/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/BasicMinimalApiWithValidation.cs index 550bed789069..4d9e0c25c6d7 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/BasicMinimalApiWithValidation.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.NativeAotTests/BasicMinimalApiWithValidation.cs @@ -4,14 +4,22 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Net.Http; +using System.Text.Json.Serialization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Validation; var builder = WebApplication.CreateSlimBuilder(); builder.Services.AddValidation(); +builder.WebHost.UseUrls("http://localhost:5000"); +builder.Services.ConfigureHttpJsonOptions(options => +{ + options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); +}); var app = builder.Build(); @@ -42,7 +50,67 @@ TypedResults.Ok(new BulkProductResponse("Bulk products created", products.Length))) .DisableValidation(); -app.Run(); +await app.StartAsync().ConfigureAwait(false); + +try +{ + // Create an HTTP client to test the endpoints + using var httpClient = new HttpClient(); + httpClient.BaseAddress = new Uri("http://localhost:5000"); + + // Test 1: Valid ID - should succeed + var response = await httpClient.GetAsync("/customers/123").ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + Console.WriteLine($"Test 1 Success: {content}"); + } + else + { + Console.WriteLine($"Test 1 Failed: {response.StatusCode}"); + return 1; + } + + // Test 2: Invalid ID (0) - should fail validation + try + { + await httpClient.GetAsync("/customers/0").ConfigureAwait(false); + } + catch (HttpRequestException httpEx) + { + // Handle HttpRequestException and assert on the response + if (httpEx.Data.Contains("HttpResponse")) + { + var invalidResponse = (HttpResponseMessage)httpEx.Data["HttpResponse"]; + if (invalidResponse.StatusCode == System.Net.HttpStatusCode.BadRequest) + { + var errorContent = await invalidResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Console.WriteLine($"Test 2 Success (Caught HttpRequestException with validation error): {invalidResponse.StatusCode}"); + Console.WriteLine($"Error content: {errorContent}"); + } + else + { + Console.WriteLine($"Test 2 Failed: Expected BadRequest in exception but got {invalidResponse.StatusCode}"); + return 1; + } + } + else + { + Console.WriteLine($"Test 2 Failed: HttpRequestException without HttpResponse data: {httpEx.Message}"); + return 1; + } + } +} +catch (Exception ex) +{ + Console.WriteLine($"Error: {ex.Message}"); + return 1; +} +finally +{ + await app.StopAsync().ConfigureAwait(false); +} return 100; @@ -163,4 +231,23 @@ public record AddressResponse(string Message, Address Address); public record InventoryResponse(int ProductId, string Name); -public record BulkProductResponse(string Message, int Count); \ No newline at end of file +public record BulkProductResponse(string Message, int Count); + +[JsonSerializable(typeof(Customer))] +[JsonSerializable(typeof(Product))] +[JsonSerializable(typeof(Address))] +[JsonSerializable(typeof(Order))] +[JsonSerializable(typeof(User))] +[JsonSerializable(typeof(ProductResponse))] +[JsonSerializable(typeof(AddressResponse))] +[JsonSerializable(typeof(InventoryResponse))] +[JsonSerializable(typeof(BulkProductResponse))] +[JsonSerializable(typeof(Product[]))] +[JsonSerializable(typeof(string))] +[JsonSerializable(typeof(int))] +[JsonSerializable(typeof(decimal))] +[JsonSerializable(typeof(DateTime))] +[JsonSerializable(typeof(Microsoft.AspNetCore.Http.HttpValidationProblemDetails))] +internal sealed partial class AppJsonSerializerContext : JsonSerializerContext +{ +} From f6049e9054dcac5f5ffd7cd9ede00492e7b47d12 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 10 Jul 2025 22:31:38 +0000 Subject: [PATCH 6/8] Fix whitespace and remove *.received.cs files --- eng/RequiresDelayedBuildProjects.props | 4 +- ...bute#ValidatableInfoResolver.g.received.cs | 242 --------------- ...ypes#ValidatableInfoResolver.g.received.cs | 248 ---------------- ...ject#ValidatableInfoResolver.g.received.cs | 199 ------------- ...aces#ValidatableInfoResolver.g.received.cs | 179 ------------ ...ters#ValidatableInfoResolver.g.received.cs | 197 ------------- ...ypes#ValidatableInfoResolver.g.received.cs | 229 --------------- ...ypes#ValidatableInfoResolver.g.received.cs | 275 ------------------ ...bute#ValidatableInfoResolver.g.received.cs | 242 --------------- ...ypes#ValidatableInfoResolver.g.received.cs | 170 ----------- ...ties#ValidatableInfoResolver.g.received.cs | 212 -------------- ...ypes#ValidatableInfoResolver.g.received.cs | 164 ----------- 12 files changed, 2 insertions(+), 2359 deletions(-) delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs diff --git a/eng/RequiresDelayedBuildProjects.props b/eng/RequiresDelayedBuildProjects.props index 56f65de773a9..1383ad7a677c 100644 --- a/eng/RequiresDelayedBuildProjects.props +++ b/eng/RequiresDelayedBuildProjects.props @@ -28,7 +28,7 @@ - - + + diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index bba18fd6b92f..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,242 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithInheritance)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - return true; - } - if (type == typeof(global::ComplexType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRangeAndDisplayName", - displayName: "Valid identifier" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithMemberAttributes", - displayName: "PropertyWithMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithoutMemberAttributes", - displayName: "PropertyWithoutMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubTypeWithInheritance), - name: "PropertyWithInheritance", - displayName: "PropertyWithInheritance" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::System.Collections.Generic.List), - name: "ListOfSubTypes", - displayName: "ListOfSubTypes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithCustomValidationAttribute", - displayName: "IntegerWithCustomValidationAttribute" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "PropertyWithMultipleAttributes", - displayName: "PropertyWithMultipleAttributes" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 538c884403fe..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,248 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithInheritance)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - return true; - } - if (type == typeof(global::ComplexType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRangeAndDisplayName", - displayName: "Valid identifier" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithMemberAttributes", - displayName: "PropertyWithMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithoutMemberAttributes", - displayName: "PropertyWithoutMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubTypeWithInheritance), - name: "PropertyWithInheritance", - displayName: "PropertyWithInheritance" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::System.Collections.Generic.List), - name: "ListOfSubTypes", - displayName: "ListOfSubTypes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithDerivedValidationAttribute", - displayName: "IntegerWithDerivedValidationAttribute" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithCustomValidation", - displayName: "IntegerWithCustomValidation" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "PropertyWithMultipleAttributes", - displayName: "PropertyWithMultipleAttributes" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 34303dad9370..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,199 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::ValidatableSubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ValidatableSubType), - members: [] - ); - return true; - } - if (type == typeof(global::ComplexValidatableType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexValidatableType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexValidatableType), - propertyType: typeof(string), - name: "Value2", - displayName: "Value2" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexValidatableType), - propertyType: typeof(global::ValidatableSubType), - name: "SubType", - displayName: "SubType" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index b77ed0ac109d..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,179 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::NamespaceOne.Type)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::NamespaceOne.Type), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::NamespaceOne.Type), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::NamespaceTwo.Type)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::NamespaceTwo.Type), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::NamespaceTwo.Type), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 49e33a093432..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,197 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::TestService)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::TestService), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::TestService), - propertyType: typeof(int), - name: "Value", - displayName: "Value" - ), - ] - ); - return true; - } - if (type == typeof(global::System.Collections.Generic.Dictionary)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::System.Collections.Generic.Dictionary), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::System.Collections.Generic.Dictionary), - propertyType: typeof(global::System.Collections.Generic.ICollection), - name: "System.Collections.Generic.IDictionary.Values", - displayName: "System.Collections.Generic.IDictionary.Values" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::System.Collections.Generic.Dictionary), - propertyType: typeof(global::System.Collections.Generic.IEnumerable), - name: "System.Collections.Generic.IReadOnlyDictionary.Values", - displayName: "System.Collections.Generic.IReadOnlyDictionary.Values" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::System.Collections.Generic.Dictionary), - propertyType: typeof(global::TestService), - name: "this[]", - displayName: "this[]" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::System.Collections.Generic.Dictionary), - propertyType: typeof(global::System.Collections.ICollection), - name: "System.Collections.IDictionary.Values", - displayName: "System.Collections.IDictionary.Values" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 0d90e4a481aa..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,229 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::DerivedType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::DerivedType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::DerivedType), - propertyType: typeof(string), - name: "Value3", - displayName: "Value3" - ), - ] - ); - return true; - } - if (type == typeof(global::BaseType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::BaseType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::BaseType), - propertyType: typeof(int), - name: "Value1", - displayName: "Value 1" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::BaseType), - propertyType: typeof(string), - name: "Value2", - displayName: "Value2" - ), - ] - ); - return true; - } - if (type == typeof(global::DerivedValidatableType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::DerivedValidatableType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::DerivedValidatableType), - propertyType: typeof(string), - name: "Value3", - displayName: "Value3" - ), - ] - ); - return true; - } - if (type == typeof(global::BaseValidatableType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::BaseValidatableType), - members: [] - ); - return true; - } - if (type == typeof(global::ContainerType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ContainerType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ContainerType), - propertyType: typeof(global::BaseType), - name: "BaseType", - displayName: "BaseType" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ContainerType), - propertyType: typeof(global::BaseValidatableType), - name: "BaseValidatableType", - displayName: "BaseValidatableType" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 786e16b5bca4..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,275 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithInheritance)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithoutConstructor)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithoutConstructor), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithoutConstructor), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithoutConstructor), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::ValidatableRecord)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ValidatableRecord), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "IntegerWithRangeAndDisplayName", - displayName: "Valid identifier" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::SubType), - name: "PropertyWithMemberAttributes", - displayName: "PropertyWithMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::SubType), - name: "PropertyWithoutMemberAttributes", - displayName: "PropertyWithoutMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::SubTypeWithInheritance), - name: "PropertyWithInheritance", - displayName: "PropertyWithInheritance" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::SubTypeWithoutConstructor), - name: "PropertyOfSubtypeWithoutConstructor", - displayName: "PropertyOfSubtypeWithoutConstructor" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::System.Collections.Generic.List), - name: "ListOfSubTypes", - displayName: "ListOfSubTypes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "IntegerWithDerivedValidationAttribute", - displayName: "IntegerWithDerivedValidationAttribute" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "IntegerWithCustomValidation", - displayName: "IntegerWithCustomValidation" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "PropertyWithMultipleAttributes", - displayName: "PropertyWithMultipleAttributes" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index bba18fd6b92f..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,242 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithInheritance)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - return true; - } - if (type == typeof(global::ComplexType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRangeAndDisplayName", - displayName: "Valid identifier" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithMemberAttributes", - displayName: "PropertyWithMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithoutMemberAttributes", - displayName: "PropertyWithoutMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubTypeWithInheritance), - name: "PropertyWithInheritance", - displayName: "PropertyWithInheritance" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::System.Collections.Generic.List), - name: "ListOfSubTypes", - displayName: "ListOfSubTypes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithCustomValidationAttribute", - displayName: "IntegerWithCustomValidationAttribute" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "PropertyWithMultipleAttributes", - displayName: "PropertyWithMultipleAttributes" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index b267e4669007..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,170 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::RecursiveType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::RecursiveType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::RecursiveType), - propertyType: typeof(int), - name: "Value", - displayName: "Value" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::RecursiveType), - propertyType: typeof(global::RecursiveType), - name: "Next", - displayName: "Next" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 18c30835da75..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,212 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::ComplexTypeWithParsableProperties)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexTypeWithParsableProperties), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.Guid?), - name: "GuidWithRegularExpression", - displayName: "GuidWithRegularExpression" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.TimeOnly?), - name: "TimeOnlyWithRequiredValue", - displayName: "TimeOnlyWithRequiredValue" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(string), - name: "Url", - displayName: "Url" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.DateOnly?), - name: "DateOnlyWithRange", - displayName: "DateOnlyWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.DateTime?), - name: "DateTimeWithRange", - displayName: "DateTimeWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(decimal?), - name: "DecimalWithRange", - displayName: "DecimalWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.TimeSpan?), - name: "TimeSpanWithHourRange", - displayName: "TimeSpanWithHourRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(bool), - name: "BooleanWithRange", - displayName: "BooleanWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.Version), - name: "VersionWithRegex", - displayName: "VersionWithRegex" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 9db7cb497042..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,164 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::ComplexType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file From 50332e07e618676bf9e524b83960dc5ef85a161b Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Fri, 11 Jul 2025 01:45:57 +0000 Subject: [PATCH 7/8] Update snapshots --- ...bute#ValidatableInfoResolver.g.received.cs | 242 +++++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 248 ++++++++++++++++ ...ject#ValidatableInfoResolver.g.received.cs | 199 +++++++++++++ ...aces#ValidatableInfoResolver.g.received.cs | 179 ++++++++++++ ...ters#ValidatableInfoResolver.g.received.cs | 197 +++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 229 +++++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 275 ++++++++++++++++++ ...bute#ValidatableInfoResolver.g.received.cs | 242 +++++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 170 +++++++++++ ...ties#ValidatableInfoResolver.g.received.cs | 212 ++++++++++++++ ...ypes#ValidatableInfoResolver.g.received.cs | 164 +++++++++++ 11 files changed, 2357 insertions(+) create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs create mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..bba18fd6b92f --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,242 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithInheritance)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithInheritance), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithInheritance), + propertyType: typeof(string), + name: "EmailString", + displayName: "EmailString" + ), + ] + ); + return true; + } + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplayName", + displayName: "Valid identifier" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithMemberAttributes", + displayName: "PropertyWithMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithoutMemberAttributes", + displayName: "PropertyWithoutMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubTypeWithInheritance), + name: "PropertyWithInheritance", + displayName: "PropertyWithInheritance" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::System.Collections.Generic.List), + name: "ListOfSubTypes", + displayName: "ListOfSubTypes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithCustomValidationAttribute", + displayName: "IntegerWithCustomValidationAttribute" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "PropertyWithMultipleAttributes", + displayName: "PropertyWithMultipleAttributes" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..538c884403fe --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,248 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithInheritance)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithInheritance), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithInheritance), + propertyType: typeof(string), + name: "EmailString", + displayName: "EmailString" + ), + ] + ); + return true; + } + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplayName", + displayName: "Valid identifier" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithMemberAttributes", + displayName: "PropertyWithMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithoutMemberAttributes", + displayName: "PropertyWithoutMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubTypeWithInheritance), + name: "PropertyWithInheritance", + displayName: "PropertyWithInheritance" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::System.Collections.Generic.List), + name: "ListOfSubTypes", + displayName: "ListOfSubTypes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithDerivedValidationAttribute", + displayName: "IntegerWithDerivedValidationAttribute" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithCustomValidation", + displayName: "IntegerWithCustomValidation" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "PropertyWithMultipleAttributes", + displayName: "PropertyWithMultipleAttributes" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..34303dad9370 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,199 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::ValidatableSubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ValidatableSubType), + members: [] + ); + return true; + } + if (type == typeof(global::ComplexValidatableType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexValidatableType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexValidatableType), + propertyType: typeof(string), + name: "Value2", + displayName: "Value2" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexValidatableType), + propertyType: typeof(global::ValidatableSubType), + name: "SubType", + displayName: "SubType" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..b77ed0ac109d --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,179 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::NamespaceOne.Type)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::NamespaceOne.Type), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::NamespaceOne.Type), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::NamespaceTwo.Type)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::NamespaceTwo.Type), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::NamespaceTwo.Type), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..49e33a093432 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,197 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::TestService)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::TestService), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::TestService), + propertyType: typeof(int), + name: "Value", + displayName: "Value" + ), + ] + ); + return true; + } + if (type == typeof(global::System.Collections.Generic.Dictionary)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::System.Collections.Generic.Dictionary), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::System.Collections.Generic.Dictionary), + propertyType: typeof(global::System.Collections.Generic.ICollection), + name: "System.Collections.Generic.IDictionary.Values", + displayName: "System.Collections.Generic.IDictionary.Values" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::System.Collections.Generic.Dictionary), + propertyType: typeof(global::System.Collections.Generic.IEnumerable), + name: "System.Collections.Generic.IReadOnlyDictionary.Values", + displayName: "System.Collections.Generic.IReadOnlyDictionary.Values" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::System.Collections.Generic.Dictionary), + propertyType: typeof(global::TestService), + name: "this[]", + displayName: "this[]" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::System.Collections.Generic.Dictionary), + propertyType: typeof(global::System.Collections.ICollection), + name: "System.Collections.IDictionary.Values", + displayName: "System.Collections.IDictionary.Values" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..0d90e4a481aa --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,229 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::DerivedType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::DerivedType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::DerivedType), + propertyType: typeof(string), + name: "Value3", + displayName: "Value3" + ), + ] + ); + return true; + } + if (type == typeof(global::BaseType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::BaseType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::BaseType), + propertyType: typeof(int), + name: "Value1", + displayName: "Value 1" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::BaseType), + propertyType: typeof(string), + name: "Value2", + displayName: "Value2" + ), + ] + ); + return true; + } + if (type == typeof(global::DerivedValidatableType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::DerivedValidatableType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::DerivedValidatableType), + propertyType: typeof(string), + name: "Value3", + displayName: "Value3" + ), + ] + ); + return true; + } + if (type == typeof(global::BaseValidatableType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::BaseValidatableType), + members: [] + ); + return true; + } + if (type == typeof(global::ContainerType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ContainerType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ContainerType), + propertyType: typeof(global::BaseType), + name: "BaseType", + displayName: "BaseType" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ContainerType), + propertyType: typeof(global::BaseValidatableType), + name: "BaseValidatableType", + displayName: "BaseValidatableType" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..786e16b5bca4 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,275 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithInheritance)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithInheritance), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithInheritance), + propertyType: typeof(string), + name: "EmailString", + displayName: "EmailString" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithoutConstructor)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithoutConstructor), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithoutConstructor), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithoutConstructor), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::ValidatableRecord)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ValidatableRecord), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplayName", + displayName: "Valid identifier" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::SubType), + name: "PropertyWithMemberAttributes", + displayName: "PropertyWithMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::SubType), + name: "PropertyWithoutMemberAttributes", + displayName: "PropertyWithoutMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::SubTypeWithInheritance), + name: "PropertyWithInheritance", + displayName: "PropertyWithInheritance" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::SubTypeWithoutConstructor), + name: "PropertyOfSubtypeWithoutConstructor", + displayName: "PropertyOfSubtypeWithoutConstructor" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(global::System.Collections.Generic.List), + name: "ListOfSubTypes", + displayName: "ListOfSubTypes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithDerivedValidationAttribute", + displayName: "IntegerWithDerivedValidationAttribute" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithCustomValidation", + displayName: "IntegerWithCustomValidation" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "PropertyWithMultipleAttributes", + displayName: "PropertyWithMultipleAttributes" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..bba18fd6b92f --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,242 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::SubType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "RequiredProperty", + displayName: "RequiredProperty" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubType), + propertyType: typeof(string), + name: "StringWithLength", + displayName: "StringWithLength" + ), + ] + ); + return true; + } + if (type == typeof(global::SubTypeWithInheritance)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::SubTypeWithInheritance), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::SubTypeWithInheritance), + propertyType: typeof(string), + name: "EmailString", + displayName: "EmailString" + ), + ] + ); + return true; + } + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplayName", + displayName: "Valid identifier" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithMemberAttributes", + displayName: "PropertyWithMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubType), + name: "PropertyWithoutMemberAttributes", + displayName: "PropertyWithoutMemberAttributes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::SubTypeWithInheritance), + name: "PropertyWithInheritance", + displayName: "PropertyWithInheritance" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::System.Collections.Generic.List), + name: "ListOfSubTypes", + displayName: "ListOfSubTypes" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithCustomValidationAttribute", + displayName: "IntegerWithCustomValidationAttribute" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "PropertyWithMultipleAttributes", + displayName: "PropertyWithMultipleAttributes" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..b267e4669007 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,170 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::RecursiveType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::RecursiveType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::RecursiveType), + propertyType: typeof(int), + name: "Value", + displayName: "Value" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::RecursiveType), + propertyType: typeof(global::RecursiveType), + name: "Next", + displayName: "Next" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..18c30835da75 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,212 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::ComplexTypeWithParsableProperties)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexTypeWithParsableProperties), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.Guid?), + name: "GuidWithRegularExpression", + displayName: "GuidWithRegularExpression" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.TimeOnly?), + name: "TimeOnlyWithRequiredValue", + displayName: "TimeOnlyWithRequiredValue" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(string), + name: "Url", + displayName: "Url" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.DateOnly?), + name: "DateOnlyWithRange", + displayName: "DateOnlyWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.DateTime?), + name: "DateTimeWithRange", + displayName: "DateTimeWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(decimal?), + name: "DecimalWithRange", + displayName: "DecimalWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.TimeSpan?), + name: "TimeSpanWithHourRange", + displayName: "TimeSpanWithHourRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(bool), + name: "BooleanWithRange", + displayName: "BooleanWithRange" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithParsableProperties), + propertyType: typeof(global::System.Version), + name: "VersionWithRegex", + displayName: "VersionWithRegex" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs new file mode 100644 index 000000000000..9db7cb497042 --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs @@ -0,0 +1,164 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) { } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _cache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + } +} \ No newline at end of file From f692652b207220ae51c34e63072ecbae53740d79 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Fri, 11 Jul 2025 16:28:30 +0000 Subject: [PATCH 8/8] Remove received files --- ...bute#ValidatableInfoResolver.g.received.cs | 242 --------------- ...bute#ValidatableInfoResolver.g.verified.cs | 6 +- ...ypes#ValidatableInfoResolver.g.received.cs | 248 ---------------- ...ypes#ValidatableInfoResolver.g.verified.cs | 6 +- ...ject#ValidatableInfoResolver.g.received.cs | 199 ------------- ...ject#ValidatableInfoResolver.g.verified.cs | 6 +- ...aces#ValidatableInfoResolver.g.received.cs | 179 ------------ ...aces#ValidatableInfoResolver.g.verified.cs | 6 +- ...ters#ValidatableInfoResolver.g.received.cs | 197 ------------- ...ters#ValidatableInfoResolver.g.verified.cs | 6 +- ...ypes#ValidatableInfoResolver.g.received.cs | 229 --------------- ...ypes#ValidatableInfoResolver.g.verified.cs | 6 +- ...ypes#ValidatableInfoResolver.g.received.cs | 275 ------------------ ...ypes#ValidatableInfoResolver.g.verified.cs | 6 +- ...bute#ValidatableInfoResolver.g.received.cs | 242 --------------- ...bute#ValidatableInfoResolver.g.verified.cs | 6 +- ...ypes#ValidatableInfoResolver.g.received.cs | 170 ----------- ...ypes#ValidatableInfoResolver.g.verified.cs | 6 +- ...ties#ValidatableInfoResolver.g.received.cs | 212 -------------- ...ties#ValidatableInfoResolver.g.verified.cs | 6 +- ...ypes#ValidatableInfoResolver.g.received.cs | 164 ----------- ...ypes#ValidatableInfoResolver.g.verified.cs | 6 +- 22 files changed, 55 insertions(+), 2368 deletions(-) delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs delete mode 100644 src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index bba18fd6b92f..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,242 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithInheritance)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - return true; - } - if (type == typeof(global::ComplexType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRangeAndDisplayName", - displayName: "Valid identifier" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithMemberAttributes", - displayName: "PropertyWithMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithoutMemberAttributes", - displayName: "PropertyWithoutMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubTypeWithInheritance), - name: "PropertyWithInheritance", - displayName: "PropertyWithInheritance" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::System.Collections.Generic.List), - name: "ListOfSubTypes", - displayName: "ListOfSubTypes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithCustomValidationAttribute", - displayName: "IntegerWithCustomValidationAttribute" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "PropertyWithMultipleAttributes", - displayName: "PropertyWithMultipleAttributes" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs index 8d1e90a957e9..bba18fd6b92f 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs @@ -188,7 +188,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 538c884403fe..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,248 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithInheritance)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - return true; - } - if (type == typeof(global::ComplexType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRangeAndDisplayName", - displayName: "Valid identifier" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithMemberAttributes", - displayName: "PropertyWithMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithoutMemberAttributes", - displayName: "PropertyWithoutMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubTypeWithInheritance), - name: "PropertyWithInheritance", - displayName: "PropertyWithInheritance" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::System.Collections.Generic.List), - name: "ListOfSubTypes", - displayName: "ListOfSubTypes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithDerivedValidationAttribute", - displayName: "IntegerWithDerivedValidationAttribute" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithCustomValidation", - displayName: "IntegerWithCustomValidation" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "PropertyWithMultipleAttributes", - displayName: "PropertyWithMultipleAttributes" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs index dcfb9562e0e9..538c884403fe 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs @@ -194,7 +194,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 34303dad9370..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,199 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::ValidatableSubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ValidatableSubType), - members: [] - ); - return true; - } - if (type == typeof(global::ComplexValidatableType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexValidatableType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexValidatableType), - propertyType: typeof(string), - name: "Value2", - displayName: "Value2" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexValidatableType), - propertyType: typeof(global::ValidatableSubType), - name: "SubType", - displayName: "SubType" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs index 9b25b7bf6e28..34303dad9370 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs @@ -145,7 +145,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index b77ed0ac109d..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,179 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::NamespaceOne.Type)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::NamespaceOne.Type), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::NamespaceOne.Type), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::NamespaceTwo.Type)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::NamespaceTwo.Type), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::NamespaceTwo.Type), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs index a44d3f565f72..b77ed0ac109d 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs @@ -125,7 +125,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 49e33a093432..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,197 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::TestService)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::TestService), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::TestService), - propertyType: typeof(int), - name: "Value", - displayName: "Value" - ), - ] - ); - return true; - } - if (type == typeof(global::System.Collections.Generic.Dictionary)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::System.Collections.Generic.Dictionary), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::System.Collections.Generic.Dictionary), - propertyType: typeof(global::System.Collections.Generic.ICollection), - name: "System.Collections.Generic.IDictionary.Values", - displayName: "System.Collections.Generic.IDictionary.Values" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::System.Collections.Generic.Dictionary), - propertyType: typeof(global::System.Collections.Generic.IEnumerable), - name: "System.Collections.Generic.IReadOnlyDictionary.Values", - displayName: "System.Collections.Generic.IReadOnlyDictionary.Values" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::System.Collections.Generic.Dictionary), - propertyType: typeof(global::TestService), - name: "this[]", - displayName: "this[]" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::System.Collections.Generic.Dictionary), - propertyType: typeof(global::System.Collections.ICollection), - name: "System.Collections.IDictionary.Values", - displayName: "System.Collections.IDictionary.Values" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs index b6fed758e501..49e33a093432 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs @@ -143,7 +143,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 0d90e4a481aa..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,229 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::DerivedType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::DerivedType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::DerivedType), - propertyType: typeof(string), - name: "Value3", - displayName: "Value3" - ), - ] - ); - return true; - } - if (type == typeof(global::BaseType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::BaseType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::BaseType), - propertyType: typeof(int), - name: "Value1", - displayName: "Value 1" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::BaseType), - propertyType: typeof(string), - name: "Value2", - displayName: "Value2" - ), - ] - ); - return true; - } - if (type == typeof(global::DerivedValidatableType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::DerivedValidatableType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::DerivedValidatableType), - propertyType: typeof(string), - name: "Value3", - displayName: "Value3" - ), - ] - ); - return true; - } - if (type == typeof(global::BaseValidatableType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::BaseValidatableType), - members: [] - ); - return true; - } - if (type == typeof(global::ContainerType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ContainerType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ContainerType), - propertyType: typeof(global::BaseType), - name: "BaseType", - displayName: "BaseType" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ContainerType), - propertyType: typeof(global::BaseValidatableType), - name: "BaseValidatableType", - displayName: "BaseValidatableType" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs index f37634f9768e..0d90e4a481aa 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs @@ -175,7 +175,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 786e16b5bca4..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,275 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithInheritance)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithoutConstructor)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithoutConstructor), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithoutConstructor), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithoutConstructor), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::ValidatableRecord)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ValidatableRecord), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "IntegerWithRangeAndDisplayName", - displayName: "Valid identifier" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::SubType), - name: "PropertyWithMemberAttributes", - displayName: "PropertyWithMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::SubType), - name: "PropertyWithoutMemberAttributes", - displayName: "PropertyWithoutMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::SubTypeWithInheritance), - name: "PropertyWithInheritance", - displayName: "PropertyWithInheritance" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::SubTypeWithoutConstructor), - name: "PropertyOfSubtypeWithoutConstructor", - displayName: "PropertyOfSubtypeWithoutConstructor" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(global::System.Collections.Generic.List), - name: "ListOfSubTypes", - displayName: "ListOfSubTypes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "IntegerWithDerivedValidationAttribute", - displayName: "IntegerWithDerivedValidationAttribute" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "IntegerWithCustomValidation", - displayName: "IntegerWithCustomValidation" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ValidatableRecord), - propertyType: typeof(int), - name: "PropertyWithMultipleAttributes", - displayName: "PropertyWithMultipleAttributes" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs index b59622211415..786e16b5bca4 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs @@ -221,7 +221,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index bba18fd6b92f..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,242 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::SubType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "RequiredProperty", - displayName: "RequiredProperty" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubType), - propertyType: typeof(string), - name: "StringWithLength", - displayName: "StringWithLength" - ), - ] - ); - return true; - } - if (type == typeof(global::SubTypeWithInheritance)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - return true; - } - if (type == typeof(global::ComplexType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRangeAndDisplayName", - displayName: "Valid identifier" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithMemberAttributes", - displayName: "PropertyWithMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubType), - name: "PropertyWithoutMemberAttributes", - displayName: "PropertyWithoutMemberAttributes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::SubTypeWithInheritance), - name: "PropertyWithInheritance", - displayName: "PropertyWithInheritance" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(global::System.Collections.Generic.List), - name: "ListOfSubTypes", - displayName: "ListOfSubTypes" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithCustomValidationAttribute", - displayName: "IntegerWithCustomValidationAttribute" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "PropertyWithMultipleAttributes", - displayName: "PropertyWithMultipleAttributes" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs index 8d1e90a957e9..bba18fd6b92f 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs @@ -188,7 +188,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index b267e4669007..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,170 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::RecursiveType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::RecursiveType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::RecursiveType), - propertyType: typeof(int), - name: "Value", - displayName: "Value" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::RecursiveType), - propertyType: typeof(global::RecursiveType), - name: "Next", - displayName: "Next" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs index 2d95a63f3199..b267e4669007 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs @@ -116,7 +116,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 18c30835da75..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,212 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::ComplexTypeWithParsableProperties)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexTypeWithParsableProperties), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.Guid?), - name: "GuidWithRegularExpression", - displayName: "GuidWithRegularExpression" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.TimeOnly?), - name: "TimeOnlyWithRequiredValue", - displayName: "TimeOnlyWithRequiredValue" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(string), - name: "Url", - displayName: "Url" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.DateOnly?), - name: "DateOnlyWithRange", - displayName: "DateOnlyWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.DateTime?), - name: "DateTimeWithRange", - displayName: "DateTimeWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(decimal?), - name: "DecimalWithRange", - displayName: "DecimalWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.TimeSpan?), - name: "TimeSpanWithHourRange", - displayName: "TimeSpanWithHourRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(bool), - name: "BooleanWithRange", - displayName: "BooleanWithRange" - ), - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexTypeWithParsableProperties), - propertyType: typeof(global::System.Version), - name: "VersionWithRegex", - displayName: "VersionWithRegex" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs index 89a27222c154..18c30835da75 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs @@ -158,7 +158,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs deleted file mode 100644 index 9db7cb497042..000000000000 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.received.cs +++ /dev/null @@ -1,164 +0,0 @@ -//HintName: ValidatableInfoResolver.g.cs -#nullable enable annotations -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -#nullable enable -#pragma warning disable ASP0029 - -namespace System.Runtime.CompilerServices -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - file sealed class InterceptsLocationAttribute : System.Attribute - { - public InterceptsLocationAttribute(int version, string data) - { - } - } -} - -namespace Microsoft.Extensions.Validation.Generated -{ - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo - { - public GeneratedValidatablePropertyInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - global::System.Type propertyType, - string name, - string displayName) : base(containingType, propertyType, name, displayName) - { - ContainingType = containingType; - Name = name; - } - - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - internal global::System.Type ContainingType { get; } - internal string Name { get; } - - protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo - { - public GeneratedValidatableTypeInfo( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] - global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver - { - public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - if (type == typeof(global::ComplexType)) - { - validatableInfo = new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - ] - ); - return true; - } - - return false; - } - - // No-ops, rely on runtime code for ParameterInfo-based resolution - public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) - { - validatableInfo = null; - return false; - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class GeneratedServiceCollectionExtensions - { - [InterceptsLocation] - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) - { - // Use non-extension method to avoid infinite recursion. - return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => - { - options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); - if (configureOptions is not null) - { - configureOptions(options); - } - }); - } - } - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] - file static class ValidationAttributeCache - { - private sealed record CacheKey( - [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type ContainingType, - string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); - - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( - [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] - global::System.Type containingType, - string propertyName) - { - var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => - { - var results = new global::System.Collections.Generic.List(); - - // Get attributes from the property - var property = k.ContainingType.GetProperty(k.PropertyName); - if (property != null) - { - var propertyAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(property, inherit: true); - - results.AddRange(propertyAttributes); - } - - // Check constructors for parameters that match the property name - // to handle record scenarios - foreach (var constructor in k.ContainingType.GetConstructors()) - { - // Look for parameter with matching name (case insensitive) - var parameter = global::System.Linq.Enumerable.FirstOrDefault( - constructor.GetParameters(), - p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); - - if (parameter != null) - { - var paramAttributes = global::System.Reflection.CustomAttributeExtensions - .GetCustomAttributes(parameter, inherit: true); - - results.AddRange(paramAttributes); - - break; - } - } - - return results.ToArray(); - }); - } - } -} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs index f8158b6baef8..9db7cb497042 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs @@ -110,7 +110,11 @@ file static class GeneratedServiceCollectionExtensions [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] file static class ValidationAttributeCache { - private sealed record CacheKey([property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes(