diff --git a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Emitters/ValidationsGenerator.Emitter.cs b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Emitters/ValidationsGenerator.Emitter.cs index 4d90dfbb5b1a..9ff704d8fcf2 100644 --- a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Emitters/ValidationsGenerator.Emitter.cs +++ b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Emitters/ValidationsGenerator.Emitter.cs @@ -7,7 +7,6 @@ using System.Text; using Microsoft.CodeAnalysis.CSharp; using System.IO; -using System.Text.RegularExpressions; namespace Microsoft.AspNetCore.Http.ValidationsGenerator; @@ -15,7 +14,6 @@ public sealed partial class ValidationsGenerator : IIncrementalGenerator { public static string GeneratedCodeConstructor => $@"global::System.CodeDom.Compiler.GeneratedCodeAttribute(""{typeof(ValidationsGenerator).Assembly.FullName}"", ""{typeof(ValidationsGenerator).Assembly.GetName().Version}"")"; public static string GeneratedCodeAttribute => $"[{GeneratedCodeConstructor}]"; - private static readonly Regex InvalidNameCharsRegex = new("[^0-9A-Za-z_]", RegexOptions.Compiled); internal static void Emit(SourceProductionContext context, (InterceptableLocation? AddValidation, ImmutableArray ValidatableTypes) emitInputs) { @@ -102,8 +100,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - -{{EmitCreateMethods(validatableTypes)}} } {{GeneratedCodeAttribute}} @@ -186,24 +182,9 @@ private static string EmitTypeChecks(ImmutableArray validatable var typeName = validatableType.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); cw.WriteLine($"if (type == typeof({typeName}))"); cw.StartBlock(); - cw.WriteLine($"validatableInfo = Create{SanitizeTypeName(validatableType.Type.MetadataName)}();"); - cw.WriteLine("return true;"); - cw.EndBlock(); - } - return sw.ToString(); - } - - private static string EmitCreateMethods(ImmutableArray validatableTypes) - { - var sw = new StringWriter(); - var cw = new CodeWriter(sw, baseIndent: 2); - foreach (var validatableType in validatableTypes) - { - cw.WriteLine($@"private ValidatableTypeInfo Create{SanitizeTypeName(validatableType.Type.MetadataName)}()"); - cw.StartBlock(); - cw.WriteLine("return new GeneratedValidatableTypeInfo("); + cw.WriteLine($"validatableInfo = new GeneratedValidatableTypeInfo("); cw.Indent++; - cw.WriteLine($"type: typeof({validatableType.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}),"); + cw.WriteLine($"type: typeof({typeName}),"); if (validatableType.Members.IsDefaultOrEmpty) { cw.WriteLine("members: []"); @@ -221,6 +202,7 @@ private static string EmitCreateMethods(ImmutableArray validata } cw.Indent--; cw.WriteLine(");"); + cw.WriteLine("return true;"); cw.EndBlock(); } return sw.ToString(); @@ -237,10 +219,4 @@ private static void EmitValidatableMemberForCreate(ValidatableProperty member, C cw.Indent--; cw.WriteLine("),"); } - - private static string SanitizeTypeName(string typeName) - { - // Replace invalid characters with underscores - return InvalidNameCharsRegex.Replace(typeName, "_"); - } } diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/ValidationsGenerator.MultipleNamespaces.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/ValidationsGenerator.MultipleNamespaces.cs new file mode 100644 index 000000000000..58478ece957e --- /dev/null +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/ValidationsGenerator.MultipleNamespaces.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.ValidationsGenerator.Tests; + +public partial class ValidationsGeneratorTests : ValidationsGeneratorTestBase +{ + [Fact] + public async Task CanValidateMultipleNamespaces() + { + // Arrange + var source = """ +using System; +using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Validation; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(); + +builder.Services.AddValidation(); + +var app = builder.Build(); + +app.MapPost("/namespace-one", (NamespaceOne.Type obj) => Results.Ok("Passed")); +app.MapPost("/namespace-two", (NamespaceTwo.Type obj) => Results.Ok("Passed")); + +app.Run(); + +namespace NamespaceOne { + public class Type + { + [StringLength(10)] + public string StringWithLength { get; set; } = string.Empty; + } +} + +namespace NamespaceTwo { + public class Type + { + [StringLength(20)] + public string StringWithLength { get; set; } = string.Empty; + } +} +"""; + await Verify(source, out var compilation); + await VerifyEndpoint(compilation, "/namespace-one", async (endpoint, serviceProvider) => + { + await InvalidStringWithLengthProducesError(endpoint); + await ValidInputProducesNoWarnings(endpoint); + + async Task InvalidStringWithLengthProducesError(Endpoint endpoint) + { + var payload = """ + { + "StringWithLength": "abcdefghijk" + } + """; + var context = CreateHttpContextWithPayload(payload, serviceProvider); + + await endpoint.RequestDelegate(context); + + var problemDetails = await AssertBadRequest(context); + Assert.Collection(problemDetails.Errors, kvp => + { + Assert.Equal("StringWithLength", kvp.Key); + Assert.Equal("The field StringWithLength must be a string with a maximum length of 10.", kvp.Value.Single()); + }); + } + + async Task ValidInputProducesNoWarnings(Endpoint endpoint) + { + var payload = """ + { + "StringWithLength": "abc" + } + """; + var context = CreateHttpContextWithPayload(payload, serviceProvider); + await endpoint.RequestDelegate(context); + + Assert.Equal(200, context.Response.StatusCode); + } + }); + await VerifyEndpoint(compilation, "/namespace-two", async (endpoint, serviceProvider) => + { + await InvalidStringWithLengthProducesError(endpoint); + await ValidInputProducesNoWarnings(endpoint); + + async Task InvalidStringWithLengthProducesError(Endpoint endpoint) + { + var payload = """ + { + "StringWithLength": "abcdefghijklmnopqrstu" + } + """; + var context = CreateHttpContextWithPayload(payload, serviceProvider); + + await endpoint.RequestDelegate(context); + + var problemDetails = await AssertBadRequest(context); + Assert.Collection(problemDetails.Errors, kvp => + { + Assert.Equal("StringWithLength", kvp.Key); + Assert.Equal("The field StringWithLength must be a string with a maximum length of 20.", kvp.Value.Single()); + }); + } + + async Task ValidInputProducesNoWarnings(Endpoint endpoint) + { + var payload = """ + { + "StringWithLength": "abcdefghijk" + } + """; + var context = CreateHttpContextWithPayload(payload, serviceProvider); + await endpoint.RequestDelegate(context); + + Assert.Equal(200, context.Response.StatusCode); + } + }); + } +} diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs index 74899270ca09..5105b0ca70cd 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs @@ -64,17 +64,101 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::SubType)) { - validatableInfo = CreateSubType(); + 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 = CreateSubTypeWithInheritance(); + 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 = CreateComplexType(); + 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; } @@ -87,104 +171,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateSubType() - { - return 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" - ), - ] - ); - } - private ValidatableTypeInfo CreateSubTypeWithInheritance() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - } - private ValidatableTypeInfo CreateComplexType() - { - return 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" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs index 997eecca5ec9..6323e4ce9ff8 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs @@ -64,17 +64,52 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::SubType)) { - validatableInfo = CreateSubType(); + 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 = CreateValidatableSubType(); + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ValidatableSubType), + members: [] + ); return true; } if (type == typeof(global::ComplexValidatableType)) { - validatableInfo = CreateComplexValidatableType(); + 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; } @@ -87,55 +122,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateSubType() - { - return 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" - ), - ] - ); - } - private ValidatableTypeInfo CreateValidatableSubType() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::ValidatableSubType), - members: [] - ); - } - private ValidatableTypeInfo CreateComplexValidatableType() - { - return 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" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs new file mode 100644 index 000000000000..ccf60a0a1c89 --- /dev/null +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs @@ -0,0 +1,175 @@ +//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.AspNetCore.Http.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.AspNetCore.Http.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] + 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)] + 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.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.AspNetCore.Http.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.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.AspNetCore.Http.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.AspNetCore.Http.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.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.AspNetCore.Http.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 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.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/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs index 97eeacbed294..d962c3758088 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs @@ -64,12 +64,50 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::TestService)) { - validatableInfo = CreateTestService(); + 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 = CreateDictionary_2(); + 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; } @@ -82,54 +120,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateTestService() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::TestService), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::TestService), - propertyType: typeof(int), - name: "Value", - displayName: "Value" - ), - ] - ); - } - private ValidatableTypeInfo CreateDictionary_2() - { - return 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" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs index b85bdf853f4e..c6cd2b9a6aeb 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs @@ -64,27 +64,82 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::DerivedType)) { - validatableInfo = CreateDerivedType(); + 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 = CreateBaseType(); + 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 = CreateDerivedValidatableType(); + 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 = CreateBaseValidatableType(); + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::BaseValidatableType), + members: [] + ); return true; } if (type == typeof(global::ContainerType)) { - validatableInfo = CreateContainerType(); + 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; } @@ -97,83 +152,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateDerivedType() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::DerivedType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::DerivedType), - propertyType: typeof(string), - name: "Value3", - displayName: "Value3" - ), - ] - ); - } - private ValidatableTypeInfo CreateBaseType() - { - return 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" - ), - ] - ); - } - private ValidatableTypeInfo CreateDerivedValidatableType() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::DerivedValidatableType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::DerivedValidatableType), - propertyType: typeof(string), - name: "Value3", - displayName: "Value3" - ), - ] - ); - } - private ValidatableTypeInfo CreateBaseValidatableType() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::BaseValidatableType), - members: [] - ); - } - private ValidatableTypeInfo CreateContainerType() - { - return 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" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs index 03a4b0af0664..29b8c10bb51e 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs @@ -64,22 +64,128 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::SubType)) { - validatableInfo = CreateSubType(); + 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 = CreateSubTypeWithInheritance(); + 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 = CreateSubTypeWithoutConstructor(); + 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 = CreateValidatableRecord(); + 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; } @@ -92,130 +198,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateSubType() - { - return 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" - ), - ] - ); - } - private ValidatableTypeInfo CreateSubTypeWithInheritance() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - } - private ValidatableTypeInfo CreateSubTypeWithoutConstructor() - { - return 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" - ), - ] - ); - } - private ValidatableTypeInfo CreateValidatableRecord() - { - return 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" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs index 65848f1ab711..7625d563e861 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs @@ -64,7 +64,23 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::RecursiveType)) { - validatableInfo = CreateRecursiveType(); + 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; } @@ -77,28 +93,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateRecursiveType() - { - return 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" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs index 716e738ec5bf..cc22f8dc30d9 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs @@ -64,7 +64,65 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::ComplexTypeWithParsableProperties)) { - validatableInfo = CreateComplexTypeWithParsableProperties(); + 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; } @@ -77,70 +135,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateComplexTypeWithParsableProperties() - { - return 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" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypesWithAttribute#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypesWithAttribute#ValidatableInfoResolver.g.verified.cs index 34c77a5cb5f1..880d416cfc13 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypesWithAttribute#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypesWithAttribute#ValidatableInfoResolver.g.verified.cs @@ -64,17 +64,95 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::SubType)) { - validatableInfo = CreateSubType(); + 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 = CreateSubTypeWithInheritance(); + 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 = CreateComplexType(); + 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; } @@ -87,98 +165,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateSubType() - { - return 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" - ), - ] - ); - } - private ValidatableTypeInfo CreateSubTypeWithInheritance() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::SubTypeWithInheritance), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::SubTypeWithInheritance), - propertyType: typeof(string), - name: "EmailString", - displayName: "EmailString" - ), - ] - ); - } - private ValidatableTypeInfo CreateComplexType() - { - return 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" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs index fba314344011..be9ab29fec62 100644 --- a/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs @@ -64,7 +64,17 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. validatableInfo = null; if (type == typeof(global::ComplexType)) { - validatableInfo = CreateComplexType(); + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "IntegerWithRange", + displayName: "IntegerWithRange" + ), + ] + ); return true; } @@ -77,22 +87,6 @@ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterIn validatableInfo = null; return false; } - - private ValidatableTypeInfo CreateComplexType() - { - return new GeneratedValidatableTypeInfo( - type: typeof(global::ComplexType), - members: [ - new GeneratedValidatablePropertyInfo( - containingType: typeof(global::ComplexType), - propertyType: typeof(int), - name: "IntegerWithRange", - displayName: "IntegerWithRange" - ), - ] - ); - } - } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]