From 67f4f3f56f7cecfc94edf9b65e6efb05c99a2519 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 9 Jul 2024 08:46:04 -0700 Subject: [PATCH 01/17] Make ApiExplorer for minimal APIs trim-compatible --- eng/TrimmableProjects.props | 1 + .../src/Metadata/IParameterBindingMetadata.cs | 43 ++++++++ .../src/PublicAPI.Unshipped.txt | 6 + .../gen/RequestDelegateGenerator.cs | 8 +- .../gen/RequestDelegateGeneratorSources.cs | 21 ++++ .../Emitters/EmitterContext.cs | 1 + .../EndpointParameter.cs | 2 +- .../StaticRouteHandlerModel.Emitter.cs | 46 +++++++- .../src/ParameterBindingMetadata.cs | 24 ++++ .../src/RequestDelegateFactory.cs | 37 +++++-- .../test/RequestDelegateFactoryTests.cs | 22 ++-- ...ntsWithAndWithoutDiagnostics.generated.txt | 2 +- ...ion_BindAsync_NullableReturn.generated.txt | 24 +++- ...MapAction_BindAsync_Snapshot.generated.txt | 88 +++++++++++---- ...Param_ComplexReturn_Snapshot.generated.txt | 20 ++++ ...Header_ComplexTypeArrayParam.generated.txt | 19 ++++ ...der_NullableStringArrayParam.generated.txt | 19 ++++ ...licitHeader_StringArrayParam.generated.txt | 19 ++++ ...tQuery_ComplexTypeArrayParam.generated.txt | 19 ++++ ...ery_NullableStringArrayParam.generated.txt | 19 ++++ ...plicitQuery_StringArrayParam.generated.txt | 19 ++++ ...eParam_SimpleReturn_Snapshot.generated.txt | 28 ++++- ...Source_SimpleReturn_Snapshot.generated.txt | 30 ++++- ...tQuery_ComplexTypeArrayParam.generated.txt | 19 ++++ ...ery_NullableStringArrayParam.generated.txt | 19 ++++ ...gArrayParam_EmptyQueryValues.generated.txt | 19 ++++ ...ngArrayParam_QueryNotPresent.generated.txt | 19 ++++ ...plicitQuery_StringArrayParam.generated.txt | 19 ++++ ...ce_HandlesBothJsonAndService.generated.txt | 22 +++- ...pecialTypeParam_StringReturn.generated.txt | 22 +++- ...ipleStringParam_StringReturn.generated.txt | 22 +++- ...aram_StringReturn_WithFilter.generated.txt | 2 +- ...n_ReturnsString_Has_Metadata.generated.txt | 2 +- ...omplexTypeParam_StringReturn.generated.txt | 21 +++- ...SingleEnumParam_StringReturn.generated.txt | 21 +++- ...ngValueProvided_StringReturn.generated.txt | 21 +++- ...MetadataEmitter_Has_Metadata.generated.txt | 19 ++++ ...AndBody_ShouldUseQueryString.generated.txt | 21 +++- ...AndBody_ShouldUseQueryString.generated.txt | 21 +++- ...String_AndBody_ShouldUseBody.generated.txt | 21 +++- ...String_AndBody_ShouldUseBody.generated.txt | 21 +++- ...String_AndBody_ShouldUseBody.generated.txt | 21 +++- ...hArrayQueryString_ShouldFail.generated.txt | 19 ++++ ...pAction_NoParam_StringReturn.generated.txt | 6 +- ...tion_WithParams_StringReturn.generated.txt | 28 ++++- ...ateValidateGeneratedFormCode.generated.txt | 23 ++++ ...InterceptorsFromSameLocation.generated.txt | 24 +++- ...terceptorsFromDifferentFiles.generated.txt | 21 +++- .../VerifyAsParametersBaseline.generated.txt | 36 +++++- .../RequestDelegateCreationTests.Metadata.cs | 26 +++-- ...egateEndpointRouteBuilderExtensionsTest.cs | 1 + .../src/ApiResponseTypeProvider.cs | 11 +- .../EndpointMetadataApiDescriptionProvider.cs | 97 ++++++++--------- ...icrosoft.AspNetCore.Mvc.ApiExplorer.csproj | 1 + ...pointMetadataApiDescriptionProviderTest.cs | 103 ++++++++++++------ 55 files changed, 1091 insertions(+), 174 deletions(-) create mode 100644 src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs create mode 100644 src/Http/Http.Extensions/src/ParameterBindingMetadata.cs diff --git a/eng/TrimmableProjects.props b/eng/TrimmableProjects.props index 6ba207ac1852..97e7a880d612 100644 --- a/eng/TrimmableProjects.props +++ b/eng/TrimmableProjects.props @@ -85,6 +85,7 @@ + diff --git a/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs new file mode 100644 index 000000000000..3c932c69f7e6 --- /dev/null +++ b/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; + +namespace Microsoft.AspNetCore.Http.Metadata; + +/// +/// Exposes metadata about the parameter binding details associated with a parameter +/// in the endpoints handler. +/// +/// +/// This metadata is injected by the RequestDelegateFactory and RequestDelegateGenerator components +/// and is primarily intended for consumption by the EndpointMetadataApiDescriptionProvider in +/// ApiExplorer. +/// +public interface IParameterBindingMetadata +{ + /// + /// The name of the parameter. + /// + string Name { get; } + + /// + /// is the parameter is associated with a type that implements IParsable or exposes a TryParse method. + /// + bool HasTryParse { get; } + + /// + /// if the parameter is associated with a type that implements a BindAsync method. + /// + bool HasBindAsync { get; } + + /// + /// The associated with the parameter. + /// + ParameterInfo ParameterInfo { get; } + + /// + /// if the parameter is optional. + /// + bool IsOptional { get; } +} diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index f31484140679..0d2aab25f97d 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -5,3 +5,9 @@ Microsoft.AspNetCore.Http.HostString.HostString(string? value) -> void Microsoft.AspNetCore.Http.HostString.Value.get -> string? Microsoft.AspNetCore.Http.HttpValidationProblemDetails.HttpValidationProblemDetails(System.Collections.Generic.IEnumerable>! errors) -> void Microsoft.AspNetCore.Http.Metadata.IDisableHttpMetricsMetadata +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.HasBindAsync.get -> bool +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.HasTryParse.get -> bool +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.IsOptional.get -> bool +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.Name.get -> string! +Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata.ParameterInfo.get -> System.Reflection.ParameterInfo! diff --git a/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs b/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs index ac8bbdd3a175..f4e2ad0e8e33 100644 --- a/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs +++ b/src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs @@ -5,14 +5,12 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Text; using Microsoft.AspNetCore.Analyzers.Infrastructure; using Microsoft.AspNetCore.App.Analyzers.Infrastructure; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel.Emitters; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Operations; namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator; @@ -243,6 +241,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var hasJsonBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBody || endpoint.EmitterContext.HasJsonBodyOrService || endpoint.EmitterContext.HasJsonBodyOrQuery); var hasResponseMetadata = endpoints.Any(endpoint => endpoint.EmitterContext.HasResponseMetadata); var requiresPropertyAsParameterInfo = endpoints.Any(endpoint => endpoint.EmitterContext.RequiresPropertyAsParameterInfo); + var requiresParameterBindingMetadataClass = endpoints.Any(endpoint => endpoint.EmitterContext.RequiresParameterBindingMetadataClass); using var stringWriter = new StringWriter(CultureInfo.InvariantCulture); using var codeWriter = new CodeWriter(stringWriter, baseIndent: 0); @@ -262,6 +261,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) codeWriter.WriteLine(RequestDelegateGeneratorSources.PropertyAsParameterInfoClass); } + if (requiresParameterBindingMetadataClass) + { + codeWriter.WriteLine(RequestDelegateGeneratorSources.ParameterBindingMetadataClass); + } + return stringWriter.ToString(); }); diff --git a/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs b/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs index b3e629a5cdee..a03eb4fa2417 100644 --- a/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs +++ b/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs @@ -449,6 +449,27 @@ public override bool IsDefined(Type attributeType, bool inherit) } """; + public static string ParameterBindingMetadataClass = $$""" + {{GeneratedCodeAttribute}} + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } +"""; + public static string AntiforgeryMetadataType = """ file sealed class AntiforgeryMetadata : IAntiforgeryMetadata { diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs index 43c0906df211..ce950dc54bfe 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs @@ -12,6 +12,7 @@ internal sealed class EmitterContext public bool HasBindAsync { get; set; } public bool HasParsable { get; set; } public bool RequiresPropertyAsParameterInfo { get; set; } + public bool RequiresParameterBindingMetadataClass { get; set; } public bool RequiresLoggingHelper { get; set; } public bool HasEndpointMetadataProvider { get; set; } public bool HasEndpointParameterMetadataProvider { get; set; } diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs index 4854c72937d2..1eca598bc655 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs @@ -47,7 +47,7 @@ private EndpointParameter(Endpoint endpoint, IPropertySymbol property, IParamete PropertyAsParameterInfoConstruction = parameter is not null ? $"new PropertyAsParameterInfo({(IsOptional ? "true" : "false")}, {propertyInfo}, {parameter.GetParameterInfoFromConstructorCode()})" : $"new PropertyAsParameterInfo({(IsOptional ? "true" : "false")}, {propertyInfo})"; - endpoint.EmitterContext.RequiresPropertyAsParameterInfo = IsProperty && IsEndpointParameterMetadataProvider; + endpoint.EmitterContext.RequiresPropertyAsParameterInfo = IsProperty; ProcessEndpointParameterSource(endpoint, property, attributeBuilder.ToImmutable(), wellKnownTypes); } diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs index 7a206872d797..1bfed13b6c58 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel.Emitters; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel; @@ -199,21 +200,26 @@ public static void EmitFilteredRequestHandler(this Endpoint endpoint, CodeWriter private static void EmitBuiltinResponseTypeMetadata(this Endpoint endpoint, CodeWriter codeWriter) { - if (endpoint.Response is not { } response || response.ResponseType is not { } responseType) + if (endpoint.Response is not { } response) { return; } - if (response.HasNoResponse || response.IsIResult) + if (!endpoint.Response.IsAwaitable && (response.HasNoResponse || response.IsIResult)) { return; } - if (responseType.SpecialType == SpecialType.System_String) + endpoint.EmitterContext.HasResponseMetadata = true; + if (response.ResponseType?.SpecialType == SpecialType.System_String) { - codeWriter.WriteLine("options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType));"); + codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType));"); } - else + else if (response.IsAwaitable && response.ResponseType == null) + { + codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(void), contentTypes: GeneratedMetadataConstants.JsonContentType));"); + } + else if (response.ResponseType is { } responseType) { codeWriter.WriteLine($$"""options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof({{responseType.ToDisplayString(EmitterConstants.DisplayFormatWithoutNullability)}}), contentTypes: GeneratedMetadataConstants.JsonContentType));"""); } @@ -358,9 +364,39 @@ public static void EmitAcceptsMetadata(this Endpoint endpoint, CodeWriter codeWr } } + public static void EmitParameterBindingMetadata(this Endpoint endpoint, CodeWriter codeWriter) + { + foreach (var parameter in endpoint.Parameters) + { + endpoint.EmitterContext.RequiresParameterBindingMetadataClass = true; + if (parameter.EndpointParameters is not null) + { + foreach (var propertyAsParameter in parameter.EndpointParameters) + { + EmitParameterBindingMetadataForParameter(propertyAsParameter, codeWriter); + } + } + else + { + EmitParameterBindingMetadataForParameter(parameter, codeWriter); + } + } + + static void EmitParameterBindingMetadataForParameter(EndpointParameter parameter, CodeWriter codeWriter) + { + var parameterName = SymbolDisplay.FormatLiteral(parameter.SymbolName, true); + var parameterInfo = parameter.IsProperty ? parameter.PropertyAsParameterInfoConstruction : $"methodInfo.GetParameters()[{parameter.Ordinal}]"; + var hasTryParse = parameter.IsParsable ? "true" : "false"; + var hasBindAsync = parameter.Source == EndpointParameterSource.BindAsync ? "true" : "false"; + var isOptional = parameter.IsOptional ? "true" : "false"; + codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata({parameterName}, {parameterInfo}, hasTryParse: {hasTryParse}, hasBindAsync: {hasBindAsync}, isOptional: {isOptional}));"); + } + } + public static void EmitEndpointMetadataPopulation(this Endpoint endpoint, CodeWriter codeWriter) { endpoint.EmitAcceptsMetadata(codeWriter); + endpoint.EmitParameterBindingMetadata(codeWriter); endpoint.EmitBuiltinResponseTypeMetadata(codeWriter); endpoint.EmitCallsToMetadataProvidersForParameters(codeWriter); endpoint.EmitCallToMetadataProviderForResponse(codeWriter); diff --git a/src/Http/Http.Extensions/src/ParameterBindingMetadata.cs b/src/Http/Http.Extensions/src/ParameterBindingMetadata.cs new file mode 100644 index 000000000000..6bc15b868aef --- /dev/null +++ b/src/Http/Http.Extensions/src/ParameterBindingMetadata.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; + +namespace Microsoft.AspNetCore.Http.Metadata; + +internal sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : IParameterBindingMetadata +{ + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; +} diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index c2e2b1ea0d28..7d532d8110d8 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -648,8 +648,18 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Request for (var i = 0; i < parameters.Length; i++) { - args[i] = CreateArgument(parameters[i], factoryContext); + args[i] = CreateArgument(parameters[i], factoryContext, out var hasTryParse, out var hasBindAsync, out var isAsParameters); + if (!isAsParameters) + { + factoryContext.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata( + name: parameters[i].Name!, + parameterInfo: parameters[i], + hasTryParse: hasTryParse, + hasBindAsync: hasBindAsync, + isOptional: IsOptionalParameter(parameters[i], factoryContext) + )); + } factoryContext.ArgumentTypes[i] = parameters[i].ParameterType; factoryContext.BoxedArgs[i] = Expression.Convert(args[i], typeof(object)); } @@ -675,8 +685,11 @@ private static Expression[] CreateArguments(ParameterInfo[]? parameters, Request return args; } - private static Expression CreateArgument(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext) + private static Expression CreateArgument(ParameterInfo parameter, RequestDelegateFactoryContext factoryContext, out bool hasTryParse, out bool hasBindAsync, out bool isAsParameters) { + hasTryParse = false; + hasBindAsync = false; + isAsParameters = false; if (parameter.Name is null) { throw new InvalidOperationException($"Encountered a parameter of type '{parameter.ParameterType}' without a name. Parameters must have a name."); @@ -772,6 +785,7 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat parameter.ParameterType == typeof(StringValues?) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType) || (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)); + hasTryParse = useSimpleBinding; return useSimpleBinding ? BindParameterFromFormItem(parameter, formAttribute.Name ?? parameter.Name, factoryContext) : BindComplexParameterFromFormItem(parameter, string.IsNullOrEmpty(formAttribute.Name) ? parameter.Name : formAttribute.Name, factoryContext); @@ -797,6 +811,7 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat } else if (parameterCustomAttributes.OfType().Any()) { + isAsParameters = true; if (parameter is PropertyAsParameterInfo) { throw new NotSupportedException( @@ -847,10 +862,12 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat } else if (ParameterBindingMethodCache.HasBindAsyncMethod(parameter)) { + hasBindAsync = true; return BindParameterFromBindAsync(parameter, factoryContext); } else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType)) { + hasTryParse = true; // 1. We bind from route values only, if route parameters are non-null and the parameter name is in that set. // 2. We bind from query only, if route parameters are non-null and the parameter name is NOT in that set. // 3. Otherwise, we fallback to route or query if route parameters is null (it means we don't know what route parameters are defined). This case only happens @@ -881,7 +898,7 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) { // We only infer parameter types if you have an array of TryParsables/string[]/StringValues/StringValues?, and DisableInferredFromBody is true - + hasTryParse = true; factoryContext.TrackedParameters.Add(parameter.Name, RequestDelegateFactoryConstants.QueryStringParameter); return BindParameterFromProperty(parameter, QueryExpr, QueryIndexerProperty, parameter.Name, factoryContext, "query string"); } @@ -1009,20 +1026,22 @@ private static void PopulateBuiltInResponseTypeMetadata(Type returnType, Endpoin throw GetUnsupportedReturnTypeException(returnType); } + var isAwaitable = false; if (CoercedAwaitableInfo.IsTypeAwaitable(returnType, out var coercedAwaitableInfo)) { returnType = coercedAwaitableInfo.AwaitableInfo.ResultType; + isAwaitable = true; } // Skip void returns and IResults. IResults might implement IEndpointMetadataProvider but otherwise we don't know what it might do. - if (returnType == typeof(void) || typeof(IResult).IsAssignableFrom(returnType)) + if (!isAwaitable && (returnType == typeof(void) || typeof(IResult).IsAssignableFrom(returnType))) { return; } if (returnType == typeof(string)) { - builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(type: null, statusCode: 200, PlaintextContentType)); + builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(type: typeof(string), statusCode: 200, PlaintextContentType)); } else { @@ -1556,8 +1575,10 @@ private static Expression BindParameterFromProperties(ParameterInfo parameter, R { var parameterInfo = new PropertyAsParameterInfo(parameters[i].PropertyInfo, parameters[i].ParameterInfo, factoryContext.NullabilityContext); - constructorArguments[i] = CreateArgument(parameterInfo, factoryContext); + Debug.Assert(parameterInfo.Name != null, "Parameter name must be set for parameters resolved from properties."); + constructorArguments[i] = CreateArgument(parameterInfo, factoryContext, out var hasTryParse, out var hasBindAsync, out var _); factoryContext.Parameters.Add(parameterInfo); + factoryContext.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata(parameterInfo.Name, parameterInfo, hasTryParse: hasTryParse, hasBindAsync: hasBindAsync, isOptional: parameterInfo.IsOptional)); } initExpression = Expression.New(constructor, constructorArguments); @@ -1579,8 +1600,10 @@ private static Expression BindParameterFromProperties(ParameterInfo parameter, R if (properties[i].CanWrite && properties[i].GetSetMethod(nonPublic: false) != null) { var parameterInfo = new PropertyAsParameterInfo(properties[i], factoryContext.NullabilityContext); - bindings.Add(Expression.Bind(properties[i], CreateArgument(parameterInfo, factoryContext))); + Debug.Assert(parameterInfo.Name != null, "Parameter name must be set for parameters resolved from properties."); + bindings.Add(Expression.Bind(properties[i], CreateArgument(parameterInfo, factoryContext, out var hasTryParse, out var hasBindAsync, out var _))); factoryContext.Parameters.Add(parameterInfo); + factoryContext.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata(parameterInfo.Name, parameterInfo, hasTryParse: hasTryParse, hasBindAsync: hasBindAsync, isOptional: parameterInfo.IsOptional)); } } diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 452eeb6c0674..2afff2e67bd7 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -2548,7 +2548,7 @@ public void Create_AddPlaintextResponseType_AsMetadata() var responseMetadata = Assert.IsAssignableFrom(Assert.Single(result.EndpointMetadata)); Assert.Equal("text/plain", Assert.Single(responseMetadata.ContentTypes)); - Assert.Null(responseMetadata.Type); + Assert.Equal(typeof(string), responseMetadata.Type); } [Fact] @@ -2567,6 +2567,7 @@ public void Create_DoesNotAddAnythingBefore_ThePassedInEndpointMetadata() // but we just specified our CustomEndpointMetadata in this test. Assert.Collection(result.EndpointMetadata, m => Assert.Same(customMetadata, m), + m => Assert.True(m is IParameterBindingMetadata { HasBindAsync : true }), m => Assert.True(m is ParameterNameMetadata { Name: "param1" }), m => Assert.True(m is CustomEndpointMetadata { Source: MetadataSource.Parameter })); } @@ -2704,8 +2705,9 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromTaskWrappedReturnTypes // Assert Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.Caller }); - // Expecting '1' because only initial metadata will be in the metadata list when this metadata item is added - Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 }); + Assert.Contains(result.EndpointMetadata, m => m is ProducesResponseTypeMetadata { Type: { } type } && type == typeof(CountsDefaultEndpointMetadataResult)); + // Expecting the custom metadata and the implicit metadata associated with a Task-based return type to be inserted + Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 2 }); } [Fact] @@ -2726,8 +2728,9 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromValueTaskWrappedReturn // Assert Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.Caller }); - // Expecting '1' because only initial metadata will be in the metadata list when this metadata item is added - Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 }); + Assert.Contains(result.EndpointMetadata, m => m is ProducesResponseTypeMetadata { Type: { } type } && type == typeof(CountsDefaultEndpointMetadataResult)); + // Expecting the custom metadata nad hte implicit metadata associated with a Task-based return type to be inserted + Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 2 }); } [Fact] @@ -2748,8 +2751,9 @@ public void Create_CombinesDefaultMetadata_AndMetadataFromFSharpAsyncWrappedRetu // Assert Assert.Contains(result.EndpointMetadata, m => m is CustomEndpointMetadata { Source: MetadataSource.Caller }); + Assert.Contains(result.EndpointMetadata, m => m is IProducesResponseTypeMetadata { Type: { } type } && type == typeof(CountsDefaultEndpointMetadataResult)); // Expecting '1' because only initial metadata will be in the metadata list when this metadata item is added - Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 1 }); + Assert.Contains(result.EndpointMetadata, m => m is MetadataCountMetadata { Count: 2 }); } [Fact] @@ -2818,6 +2822,8 @@ public void Create_CombinesAllMetadata_InCorrectOrder() m => Assert.True(m is CustomEndpointMetadata { Source: MetadataSource.Caller }), // Inferred AcceptsMetadata from RDF for complex type m => Assert.True(m is AcceptsMetadata am && am.RequestType == typeof(AddsCustomParameterMetadata)), + // Inferred ParameterBinding metadata + m => Assert.True(m is IParameterBindingMetadata { Name: "param1" }), // Inferred ProducesResopnseTypeMetadata from RDF for complex type m => Assert.Equal(typeof(CountsDefaultEndpointMetadataPoco), ((IProducesResponseTypeMetadata)m).Type), // Metadata provided by parameters implementing IEndpointParameterMetadataProvider @@ -2825,7 +2831,7 @@ public void Create_CombinesAllMetadata_InCorrectOrder() // Metadata provided by parameters implementing IEndpointMetadataProvider m => Assert.True(m is CustomEndpointMetadata { Source: MetadataSource.Parameter }), // Metadata provided by return type implementing IEndpointMetadataProvider - m => Assert.True(m is MetadataCountMetadata { Count: 5 })); + m => Assert.True(m is MetadataCountMetadata { Count: 6 })); } [Fact] @@ -2878,7 +2884,7 @@ public void Create_DoesNotInferMetadata_GivenManuallyConstructedMetadataResult() var result = RequestDelegateFactory.Create(@delegate, options, metadataResult); // Assert - Assert.Empty(result.EndpointMetadata); + Assert.Contains(result.EndpointMetadata, m => m is IParameterBindingMetadata { Name: "param1" }); Assert.Same(options.EndpointBuilder.Metadata, result.EndpointMetadata); // Make extra sure things are running as expected, as this non-InferMetadata path is no longer exercised by RouteEndpointDataSource, diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt index 4ba6654a262e..ae38db998901 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt index 81d768e52b43..b0899ef1300d 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt @@ -70,7 +70,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("param", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -180,7 +181,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("param", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -368,6 +370,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt index e584608eb59d..bef9d0d17c4e 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt @@ -69,7 +69,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -180,7 +182,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -277,7 +280,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -376,7 +381,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -473,7 +479,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -584,7 +592,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -681,7 +690,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -780,7 +791,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -877,7 +889,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -987,7 +1001,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1083,7 +1098,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1181,7 +1198,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1277,7 +1295,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1388,7 +1408,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1485,7 +1506,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1595,7 +1618,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1691,7 +1715,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1802,7 +1828,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1899,7 +1926,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: true, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -2009,7 +2038,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("myBindAsyncParam", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: true, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -2184,6 +2214,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt index c353cec04fe2..6bc90f525e6f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt @@ -70,6 +70,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: typeof(global::Microsoft.AspNetCore.Http.Generators.Tests.Todo), isOptional: false, contentTypes: GeneratedMetadataConstants.JsonContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("todo", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); PopulateMetadataForEndpoint>(methodInfo, options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -176,6 +177,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: typeof(global::Microsoft.AspNetCore.Http.Generators.Tests.Todo), isOptional: true, contentTypes: GeneratedMetadataConstants.JsonContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("todo", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); PopulateMetadataForEndpoint>(methodInfo, options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -417,6 +419,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt index 43c52222ed32..46eeaec7a821 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -266,6 +267,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt index 561816f4614b..c96684fe9251 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -238,6 +239,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt index c364250c27de..faa3efdd5a16 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -238,6 +239,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt index 59a8d8116b8c..410901937b72 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -266,6 +267,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt index 225d3564d15d..acfa3b8fb6b2 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -237,6 +238,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt index 215688d0e1e7..4db9f25b43e8 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -237,6 +238,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt index ba54b0c4ad79..53ea673aa77a 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt @@ -69,7 +69,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svc", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -167,7 +168,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svc", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -265,7 +267,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svc", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svcs", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -441,6 +445,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt index 4c9760e42208..176c9b64d329 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt @@ -69,7 +69,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("queryValue", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -181,7 +182,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("headerValue", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -293,7 +295,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("routeValue", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -414,7 +417,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("value", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -607,6 +611,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt index 1f4f87e9afca..cfb6be2aa8fd 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -273,6 +274,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt index c059050111ec..40551947460b 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt index c059050111ec..40551947460b 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt index c059050111ec..40551947460b 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt index 5b5a107e475e..666539e56e44 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt index 779efb616d06..60edaff577a4 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt @@ -85,7 +85,9 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("todo", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("svc", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -351,6 +353,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt index add8e79a859a..0183fb94ff17 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt @@ -69,7 +69,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("req", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("res", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -240,6 +242,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt index b76324c9d0f8..6cc1065ecd72 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt @@ -69,7 +69,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p1", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p2", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -273,6 +275,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt index 4ba6654a262e..ae38db998901 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt index 4ba6654a262e..ae38db998901 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt index 8a4e1c8411b1..5e1aebe69bd3 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt @@ -69,7 +69,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -273,6 +274,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt index 4c46221e27a9..ee2722bd73ea 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt @@ -69,7 +69,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -273,6 +274,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt index d3a31800ab3f..40ca9483edd8 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt @@ -69,7 +69,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -245,6 +246,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt index 9ce50bcba1fc..83b537637c4d 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("x", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); var parameterInfos = methodInfo.GetParameters(); var x_ParameterInfo = parameterInfos[0]; PopulateMetadataForParameter(x_ParameterInfo, options.EndpointBuilder); @@ -340,6 +341,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt index a4fc77ad1227..c94fd82bd797 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt index a4fc77ad1227..c94fd82bd797 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index a4fc77ad1227..c94fd82bd797 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index a4fc77ad1227..c94fd82bd797 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index 598d3543f866..3c7f24d18680 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -341,6 +342,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt index 1b03fbb80eab..ab84fbb33777 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt @@ -84,6 +84,7 @@ namespace Microsoft.AspNetCore.Http.Generated break; } } + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; @@ -334,6 +335,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt index 484aaefc7f3c..79567d9d3a09 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt @@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -255,7 +255,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt index 17fa1194e78a..2021b4a1de99 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt @@ -69,7 +69,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("req", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -164,7 +165,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("res", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -259,7 +261,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("req", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("res", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -430,6 +434,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt index a093bce3d48c..d1524be100ef 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt @@ -71,6 +71,11 @@ namespace Microsoft.AspNetCore.Http.Generated options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(AntiforgeryMetadata.ValidationRequired); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.FormFileContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("httpContext", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("file", methodInfo.GetParameters()[1], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("fileCollection", methodInfo.GetParameters()[2], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("collection", methodInfo.GetParameters()[3], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("tryParseRecord", methodInfo.GetParameters()[4], hasTryParse: true, hasBindAsync: false, isOptional: false)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -387,6 +392,24 @@ file sealed class AntiforgeryMetadata : IAntiforgeryMetadata public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt index 746ccff2f381..8cd26408a415 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt @@ -69,7 +69,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("name", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -182,7 +183,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("age", methodInfo.GetParameters()[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -393,6 +395,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt index de4a2f087ea0..a6e3f4ad40a2 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt @@ -71,7 +71,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("name", methodInfo.GetParameters()[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -264,6 +265,24 @@ namespace Microsoft.AspNetCore.Http.Generated public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" }; } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt index b26c613dc854..5567f4d61e61 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt @@ -72,6 +72,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(int) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Value", new PropertyAsParameterInfo(true, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue)!.GetProperty("Value")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(int) })?.GetParameters()[1]), hasTryParse: true, hasBindAsync: false, isOptional: true)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -208,6 +210,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct)!.GetProperty("HttpContext")!), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Value", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct)!.GetProperty("Value")!), hasTryParse: true, hasBindAsync: false, isOptional: false)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -337,6 +341,10 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options != null, "RequestDelegateFactoryOptions not found."); Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(System.Security.Claims.ClaimsPrincipal), typeof(Microsoft.AspNetCore.Http.HttpRequest), typeof(Microsoft.AspNetCore.Http.HttpResponse) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("User", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext)!.GetProperty("User")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(System.Security.Claims.ClaimsPrincipal), typeof(Microsoft.AspNetCore.Http.HttpRequest), typeof(Microsoft.AspNetCore.Http.HttpResponse) })?.GetParameters()[1]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Request", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext)!.GetProperty("Request")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(System.Security.Claims.ClaimsPrincipal), typeof(Microsoft.AspNetCore.Http.HttpRequest), typeof(Microsoft.AspNetCore.Http.HttpResponse) })?.GetParameters()[2]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Response", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext)!.GetProperty("Response")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(System.Security.Claims.ClaimsPrincipal), typeof(Microsoft.AspNetCore.Http.HttpRequest), typeof(Microsoft.AspNetCore.Http.HttpResponse) })?.GetParameters()[3]), hasTryParse: false, hasBindAsync: false, isOptional: false)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -438,7 +446,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Todo", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody)!.GetProperty("Todo")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct) })?.GetParameters()[1]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -557,6 +567,8 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Value", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("Value")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[1]), hasTryParse: false, hasBindAsync: false, isOptional: false)); var parameterInfos = methodInfo.GetParameters(); var Value_ParameterInfo = new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("Value")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[1]); PopulateMetadataForParameter(Value_ParameterInfo, options.EndpointBuilder); @@ -671,7 +683,9 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Todo", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterRecordStructWithJsonBodyOrService)!.GetProperty("Todo")!), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Service", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterRecordStructWithJsonBodyOrService)!.GetProperty("Service")!), hasTryParse: false, hasBindAsync: false, isOptional: false)); + options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -1059,6 +1073,24 @@ namespace Microsoft.AspNetCore.Http.Generated public new bool IsOptional { get; } } + %GENERATEDCODEATTRIBUTE% + file sealed class ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + { + public string Name => name; + + public bool HasTryParse => hasTryParse; + + public bool HasBindAsync => hasBindAsync; + + public ParameterInfo ParameterInfo => parameterInfo; + + public bool IsOptional => isOptional; + } %GENERATEDCODEATTRIBUTE% file sealed class LogOrThrowExceptionHelper diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs index 6be8fd336245..8d89c6503a54 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs @@ -33,7 +33,7 @@ public async Task MapAction_ReturnsString_Has_Metadata() var metadata = endpoint.Metadata.OfType().Single(); Assert.Equal(200, metadata.StatusCode); Assert.Equal("text/plain", metadata.ContentTypes.Single()); - Assert.Null(metadata.Type); + Assert.Equal(typeof(string), metadata.Type); await VerifyAgainstBaselineUsingFile(compilation); } @@ -79,19 +79,21 @@ public async Task MapAction_ReturnsTaskOfString_Has_Metadata() var metadata = endpoint.Metadata.OfType().Single(); Assert.Equal(200, metadata.StatusCode); Assert.Equal("text/plain", metadata.ContentTypes.Single()); - Assert.Null(metadata.Type); + Assert.Equal(typeof(string), metadata.Type); } [Fact] - public async Task MapAction_ReturnsTask_Has_No_Metadata() + public async Task MapAction_ReturnsTask_ProducesInferredMetadata() { var (_, compilation) = await RunGeneratorAsync(""" app.MapGet("/", Task () => Task.CompletedTask); """); var endpoint = GetEndpointFromCompilation(compilation); - var metadata = endpoint.Metadata.OfType(); - Assert.Empty(metadata); + var metadata = endpoint.Metadata.OfType().Single(); + Assert.Equal(200, metadata.StatusCode); + Assert.Equal("application/json", metadata.ContentTypes.Single()); + Assert.Equal(typeof(void), metadata.Type); } [Fact] @@ -105,19 +107,21 @@ public async Task MapAction_ReturnsValueTaskOfString_Has_Metadata() var metadata = endpoint.Metadata.OfType().Single(); Assert.Equal(200, metadata.StatusCode); Assert.Equal("text/plain", metadata.ContentTypes.Single()); - Assert.Null(metadata.Type); + Assert.Equal(typeof(string), metadata.Type); } [Fact] - public async Task MapAction_ReturnsValueTask_Has_No_Metadata() + public async Task MapAction_ReturnsValueTask_ProducesInferredMetadata() { var (_, compilation) = await RunGeneratorAsync(""" app.MapGet("/", ValueTask () => ValueTask.CompletedTask); """); var endpoint = GetEndpointFromCompilation(compilation); - var metadata = endpoint.Metadata.OfType(); - Assert.Empty(metadata); + var metadata = endpoint.Metadata.OfType().Single(); + Assert.Equal(200, metadata.StatusCode); + Assert.Equal("application/json", metadata.ContentTypes.Single()); + Assert.Equal(typeof(void), metadata.Type); } [Fact] @@ -189,7 +193,7 @@ public async Task Create_AddPlaintextResponseType_AsMetadata() var responseMetadata = endpoint.Metadata.OfType().Single(); Assert.Equal("text/plain", Assert.Single(responseMetadata.ContentTypes)); - Assert.Null(responseMetadata.Type); + Assert.Equal(typeof(string), responseMetadata.Type); } [Fact] @@ -526,6 +530,8 @@ m is not Attribute2 && Assert.Collection(filteredMetadata, // Inferred AcceptsMetadata from RDF for complex type m => Assert.True(m is IAcceptsMetadata am && am.RequestType == typeof(AddsCustomParameterMetadata)), + // Parameter binding metadata inferred by RDF + m => Assert.True(m is IParameterBindingMetadata { Name: "param1" }), // Inferred ProducesResopnseTypeMetadata from RDF for complex type m => Assert.Equal(typeof(CountsDefaultEndpointMetadataPoco), ((IProducesResponseTypeMetadata)m).Type), // Metadata provided by parameters implementing IEndpointParameterMetadataProvider diff --git a/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs b/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs index 451c408d004b..a5156c7fff05 100644 --- a/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs @@ -487,6 +487,7 @@ public void Map_AddsMetadata_InCorrectOrder() Assert.Collection(metadata, m => Assert.IsAssignableFrom(m), + m => Assert.IsAssignableFrom(m), m => Assert.IsAssignableFrom(m), m => { diff --git a/src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs index d1f151812ae6..37d7651a1081 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/ApiResponseTypeProvider.cs @@ -95,6 +95,7 @@ private ICollection GetApiResponseTypes( type, defaultErrorType, contentTypes, + out var _, responseTypeMetadataProviders); foreach (var responseType in responseTypesFromProvider) @@ -134,11 +135,13 @@ private ICollection GetApiResponseTypes( internal static Dictionary ReadResponseMetadata( IReadOnlyList responseMetadataAttributes, Type? type, - Type defaultErrorType, + Type? defaultErrorType, MediaTypeCollection contentTypes, + out bool errorSetByDefault, IEnumerable? responseTypeMetadataProviders = null, IModelMetadataProvider? modelMetadataProvider = null) { + errorSetByDefault = false; var results = new Dictionary(); // Get the content type that the action explicitly set to support. @@ -184,8 +187,8 @@ internal static Dictionary ReadResponseMetadata( { // Determine whether or not the type was provided by the user. If so, favor it over the default // error type for 4xx client errors if no response type is specified.. - var setByDefault = metadataAttribute is ProducesResponseTypeAttribute { IsResponseTypeSetByDefault: true }; - apiResponseType.Type = setByDefault ? defaultErrorType : apiResponseType.Type; + errorSetByDefault = metadataAttribute is ProducesResponseTypeAttribute { IsResponseTypeSetByDefault: true }; + apiResponseType.Type = errorSetByDefault ? defaultErrorType : apiResponseType.Type; } else if (apiResponseType.IsDefaultResponse) { @@ -233,7 +236,7 @@ internal static Dictionary ReadResponseMetadata( StatusCode = statusCode, }; - if (apiResponseType.Type == typeof(void)) + if (apiResponseType.Type == null) { if (type != null && (statusCode == StatusCodes.Status200OK || statusCode == StatusCodes.Status201Created)) { diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 8766b063f412..6e6de041d1ae 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Internal; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Mvc.ApiExplorer; @@ -25,7 +24,6 @@ internal sealed class EndpointMetadataApiDescriptionProvider : IApiDescriptionPr private readonly EndpointDataSource _endpointDataSource; private readonly IHostEnvironment _environment; private readonly IServiceProviderIsService? _serviceProviderIsService; - private readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); private readonly ParameterPolicyFactory _parameterPolicyFactory; // Executes before MVC's DefaultApiDescriptionProvider and GrpcJsonTranscodingDescriptionProvider for no particular reason. @@ -115,10 +113,11 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string }; var hasBodyOrFormFileParameter = false; + var parameters = routeEndpoint.Metadata.GetOrderedMetadata(); - foreach (var parameter in PropertyAsParameterInfo.Flatten(methodInfo.GetParameters(), ParameterBindingMethodCache)) + foreach (var parameter in parameters) { - var parameterDescription = CreateApiParameterDescription(parameter, routeEndpoint.RoutePattern, disableInferredBody); + var parameterDescription = CreateApiParameterDescription(parameter, routeEndpoint, disableInferredBody); if (parameterDescription is { }) { @@ -169,9 +168,10 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string return apiDescription; } - private ApiParameterDescription? CreateApiParameterDescription(ParameterInfo parameter, RoutePattern pattern, bool disableInferredBody) + private ApiParameterDescription? CreateApiParameterDescription(IParameterBindingMetadata parameter, RouteEndpoint routeEndpoint, bool disableInferredBody) { - var (source, name, allowEmpty, paramType) = GetBindingSourceAndName(parameter, pattern, disableInferredBody); + var pattern = routeEndpoint.RoutePattern; + var (source, name, _, paramType) = GetBindingSourceAndName(parameter, routeEndpoint, disableInferredBody); // Services are ignored because they are not request parameters. if (source == BindingSource.Services) @@ -180,21 +180,17 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string } // Determine the "requiredness" based on nullability, default value or if allowEmpty is set - var nullabilityContext = new NullabilityInfoContext(); - var nullability = nullabilityContext.Create(parameter); - var isOptional = parameter is PropertyAsParameterInfo argument - ? argument.IsOptional || allowEmpty - : parameter.HasDefaultValue || nullability.ReadState != NullabilityState.NotNull || allowEmpty; - var parameterDescriptor = CreateParameterDescriptor(parameter, pattern); - var routeInfo = CreateParameterRouteInfo(pattern, parameter, isOptional); + var isOptional = parameter.IsOptional; + var parameterDescriptor = CreateParameterDescriptor(parameter.ParameterInfo, pattern); + var routeInfo = CreateParameterRouteInfo(pattern, parameter.ParameterInfo, isOptional); return new ApiParameterDescription { Name = name, ModelMetadata = CreateModelMetadata(paramType), Source = source, - DefaultValue = parameter.DefaultValue, - Type = parameter.ParameterType, + DefaultValue = parameter.ParameterInfo.DefaultValue, + Type = parameter.ParameterInfo.ParameterType, IsRequired = !isOptional, ParameterDescriptor = parameterDescriptor, RouteInfo = routeInfo @@ -250,47 +246,48 @@ private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo param // TODO: Share more of this logic with RequestDelegateFactory.CreateArgument(...) using RequestDelegateFactoryUtilities // which is shared source. - private (BindingSource, string, bool, Type) GetBindingSourceAndName(ParameterInfo parameter, RoutePattern pattern, bool disableInferredBody) + private (BindingSource, string, bool, Type) GetBindingSourceAndName(IParameterBindingMetadata parameter, RouteEndpoint routeEndpoint, bool disableInferredBody) { - var attributes = parameter.GetCustomAttributes(); - + var pattern = routeEndpoint.RoutePattern; + var attributes = parameter.ParameterInfo.GetCustomAttributes(); + var parameterType = parameter.ParameterInfo.ParameterType; if (attributes.OfType().FirstOrDefault() is { } routeAttribute) { var parameterName = parameter.Name ?? string.Empty; var name = pattern.GetParameter(parameterName)?.Name ?? parameterName; - return (BindingSource.Path, routeAttribute.Name ?? name, false, parameter.ParameterType); + return (BindingSource.Path, routeAttribute.Name ?? name, false, parameterType); } else if (attributes.OfType().FirstOrDefault() is { } queryAttribute) { - return (BindingSource.Query, queryAttribute.Name ?? parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.Query, queryAttribute.Name ?? parameter.Name ?? string.Empty, false, parameterType); } else if (attributes.OfType().FirstOrDefault() is { } headerAttribute) { - return (BindingSource.Header, headerAttribute.Name ?? parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.Header, headerAttribute.Name ?? parameter.Name ?? string.Empty, false, parameterType); } else if (attributes.OfType().FirstOrDefault() is { } fromBodyAttribute) { - return (BindingSource.Body, parameter.Name ?? string.Empty, fromBodyAttribute.AllowEmpty, parameter.ParameterType); + return (BindingSource.Body, parameter.Name ?? string.Empty, fromBodyAttribute.AllowEmpty, parameterType); } else if (attributes.OfType().FirstOrDefault() is { } fromFormAttribute) { - return (BindingSource.FormFile, fromFormAttribute.Name ?? parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.FormFile, fromFormAttribute.Name ?? parameter.Name ?? string.Empty, false, parameterType); } - else if (parameter.CustomAttributes.Any(a => typeof(IFromServiceMetadata).IsAssignableFrom(a.AttributeType) || typeof(FromKeyedServicesAttribute) == a.AttributeType) || - parameter.ParameterType == typeof(HttpContext) || - parameter.ParameterType == typeof(HttpRequest) || - parameter.ParameterType == typeof(HttpResponse) || - parameter.ParameterType == typeof(ClaimsPrincipal) || - parameter.ParameterType == typeof(CancellationToken) || - ParameterBindingMethodCache.HasBindAsyncMethod(parameter) || - _serviceProviderIsService?.IsService(parameter.ParameterType) == true) - { - return (BindingSource.Services, parameter.Name ?? string.Empty, false, parameter.ParameterType); + else if (parameter.ParameterInfo.CustomAttributes.Any(a => typeof(IFromServiceMetadata).IsAssignableFrom(a.AttributeType) || typeof(FromKeyedServicesAttribute) == a.AttributeType) || + parameterType == typeof(HttpContext) || + parameterType == typeof(HttpRequest) || + parameterType == typeof(HttpResponse) || + parameterType == typeof(ClaimsPrincipal) || + parameterType == typeof(CancellationToken) || + parameter.HasBindAsync || + _serviceProviderIsService?.IsService(parameterType) == true) + { + return (BindingSource.Services, parameter.Name ?? string.Empty, false, parameterType); } - else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType)) + else if (parameterType == typeof(string) || (!parameterType.IsArray && parameterType != typeof(StringValues) && parameter.HasTryParse)) { // complex types will display as strings since they use custom parsing via TryParse on a string - var displayType = EndpointModelMetadata.GetDisplayType(parameter.ParameterType); + var displayType = EndpointModelMetadata.GetDisplayType(parameterType); // Path vs query cannot be determined by RequestDelegateFactory at startup currently because of the layering, but can be done here. if (parameter.Name is { } name && pattern.GetParameter(name) is { } routeParam) @@ -302,20 +299,20 @@ private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo param return (BindingSource.Query, parameter.Name ?? string.Empty, false, displayType); } } - else if (parameter.ParameterType == typeof(IFormFile) || parameter.ParameterType == typeof(IFormFileCollection)) + else if (parameterType == typeof(IFormFile) || parameterType == typeof(IFormFileCollection)) { - return (BindingSource.FormFile, parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.FormFile, parameter.Name ?? string.Empty, false, parameterType); } else if (disableInferredBody && ( - parameter.ParameterType == typeof(string[]) || - parameter.ParameterType == typeof(StringValues) || - (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)) )) + parameterType == typeof(string[]) || + parameterType == typeof(StringValues) || + (parameterType.IsArray && parameter.HasTryParse))) { - return (BindingSource.Query, parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.Query, parameter.Name ?? string.Empty, false, parameterType); } else { - return (BindingSource.Body, parameter.Name ?? string.Empty, false, parameter.ParameterType); + return (BindingSource.Body, parameter.Name ?? string.Empty, false, parameterType); } } @@ -326,11 +323,6 @@ private static void AddSupportedResponseTypes( { var responseType = returnType; - if (CoercedAwaitableInfo.IsTypeAwaitable(responseType, out var coercedAwaitableInfo)) - { - responseType = coercedAwaitableInfo.AwaitableInfo.ResultType; - } - // Can't determine anything about IResults yet that's not from extra metadata. IResult could help here. if (typeof(IResult).IsAssignableFrom(responseType)) { @@ -346,7 +338,7 @@ private static void AddSupportedResponseTypes( var contentTypes = new MediaTypeCollection(); var responseProviderMetadataTypes = ApiResponseTypeProvider.ReadResponseMetadata( - responseProviderMetadata, responseType, defaultErrorType, contentTypes); + responseProviderMetadata, responseType, defaultErrorType, contentTypes, out var errorSetByDefault); var producesResponseMetadataTypes = ApiResponseTypeProvider.ReadResponseMetadata(producesResponseMetadata, responseType); // We favor types added via the extension methods (which implements IProducesResponseTypeMetadata) @@ -357,9 +349,12 @@ private static void AddSupportedResponseTypes( { foreach (var apiResponseType in responseMetadataTypes) { - // void means no response type was specified by the metadata, so use whatever we inferred. - // ApiResponseTypeProvider should never return ApiResponseTypes with null Type, but it doesn't hurt to check. - if (apiResponseType.Type is null || apiResponseType.Type == typeof(void)) + // In some context, a typeof(void) return means that no response type was specified by the metadata. This can happen + // if a user applied a [ProducesResponseType] attribute without a default type parameter. In this case, we should use the + // response type inferred from the return type of the handler. For minimal API scenarios, where `typeof(void)` can be inferred + // by the framework for handlers that return awaitables, we will only treat `typeof(void)` as a null type that should fall back to the + // inference logic if it has been set as the default error type to retain back-compat. + if (apiResponseType.Type is null || (apiResponseType.Type == typeof(void) && errorSetByDefault)) { apiResponseType.Type = responseType; } diff --git a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj index 075d7b90a560..c097a4c40112 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj +++ b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj @@ -7,6 +7,7 @@ true aspnetcore;aspnetcoremvc false + true diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index 526f9e150bde..c16601586147 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -71,12 +71,12 @@ static void AssertCustomRequestFormat(ApiDescription apiDescription) AssertCustomRequestFormat(GetApiDescription( [Consumes("application/custom")] (InferredJsonClass fromBody) => - { })); + { }, httpMethods: ["POST"])); AssertCustomRequestFormat(GetApiDescription( [Consumes("application/custom")] ([FromBody] int fromBody) => - { })); + { }, httpMethods: ["POST"])); } [Fact] @@ -85,7 +85,7 @@ public void AddsMultipleRequestFormatsFromMetadata() var apiDescription = GetApiDescription( [Consumes("application/custom0", "application/custom1")] (InferredJsonClass fromBody) => - { }); + { }, httpMethods: ["POST"]); Assert.Equal(2, apiDescription.SupportedRequestFormats.Count); @@ -119,7 +119,7 @@ public void AddsMultipleRequestFormatsFromMetadataWithRequiredBodyParameter() var apiDescription = GetApiDescription( [Consumes(typeof(InferredJsonClass), "application/custom0", "application/custom1", IsOptional = false)] (InferredJsonClass fromBody) => - { }); + { }, httpMethods: ["POST"]); Assert.Equal(2, apiDescription.SupportedRequestFormats.Count); @@ -188,14 +188,23 @@ public void AddsResponseFormatFromMetadata() [Produces("application/custom")] () => new InferredJsonClass()); - var responseType = Assert.Single(apiDescription.SupportedResponseTypes); + Assert.Equal(2, apiDescription.SupportedResponseTypes.Count); - Assert.Equal(201, responseType.StatusCode); - Assert.Equal(typeof(TimeSpan), responseType.Type); - Assert.Equal(typeof(TimeSpan), responseType.ModelMetadata?.ModelType); + var inferredResponseType = apiDescription.SupportedResponseTypes[0]; - var responseFormat = Assert.Single(responseType.ApiResponseFormats); - Assert.Equal("application/custom", responseFormat.MediaType); + Assert.Equal(200, inferredResponseType.StatusCode); + Assert.Equal(typeof(InferredJsonClass), inferredResponseType.Type); + Assert.Equal(typeof(InferredJsonClass), inferredResponseType.ModelMetadata?.ModelType); + + Assert.Equal(["application/json", "application/custom"], inferredResponseType.ApiResponseFormats.Select(f => f.MediaType)); + + var annotatedResponseType = apiDescription.SupportedResponseTypes[1]; + + Assert.Equal(201, annotatedResponseType.StatusCode); + Assert.Equal(typeof(TimeSpan), annotatedResponseType.Type); + Assert.Equal(typeof(TimeSpan), annotatedResponseType.ModelMetadata?.ModelType); + + Assert.Equal("application/custom", Assert.Single(annotatedResponseType.ApiResponseFormats).MediaType); } [Fact] @@ -206,9 +215,18 @@ public void AddsMultipleResponseFormatsFromMetadataWithPoco() [ProducesResponseType(StatusCodes.Status400BadRequest)] () => new InferredJsonClass()); - Assert.Equal(2, apiDescription.SupportedResponseTypes.Count); + Assert.Equal(3, apiDescription.SupportedResponseTypes.Count); - var createdResponseType = apiDescription.SupportedResponseTypes[0]; + var rdfInferredResponseType = apiDescription.SupportedResponseTypes[0]; + + Assert.Equal(200, rdfInferredResponseType.StatusCode); + Assert.Equal(typeof(InferredJsonClass), rdfInferredResponseType.Type); + Assert.Equal(typeof(InferredJsonClass), rdfInferredResponseType.ModelMetadata?.ModelType); + + var rdfInferredResponseFormat = Assert.Single(rdfInferredResponseType.ApiResponseFormats); + Assert.Equal("application/json", rdfInferredResponseFormat.MediaType); + + var createdResponseType = apiDescription.SupportedResponseTypes[1]; Assert.Equal(201, createdResponseType.StatusCode); Assert.Equal(typeof(TimeSpan), createdResponseType.Type); @@ -217,7 +235,7 @@ public void AddsMultipleResponseFormatsFromMetadataWithPoco() var createdResponseFormat = Assert.Single(createdResponseType.ApiResponseFormats); Assert.Equal("application/json", createdResponseFormat.MediaType); - var badRequestResponseType = apiDescription.SupportedResponseTypes[1]; + var badRequestResponseType = apiDescription.SupportedResponseTypes[2]; Assert.Equal(400, badRequestResponseType.StatusCode); Assert.Equal(typeof(InferredJsonClass), badRequestResponseType.Type); @@ -421,8 +439,8 @@ public void AddsFromHeaderParameterAsHeader() public void DoesNotAddFromServiceParameterAsService() { Assert.Empty(GetApiDescription((IInferredServiceInterface foo) => { }).ParameterDescriptions); - Assert.Empty(GetApiDescription(([FromServices] int foo) => { }).ParameterDescriptions); - Assert.Empty(GetApiDescription(([FromKeyedServices("foo")] int foo) => { }).ParameterDescriptions); + Assert.Empty(GetApiDescription(([FromServices] InferredServiceClass foo) => { }).ParameterDescriptions); + Assert.Empty(GetApiDescription(([FromKeyedServices("foo")] InferredServiceClass foo) => { }).ParameterDescriptions); Assert.Empty(GetApiDescription((HttpContext context) => { }).ParameterDescriptions); Assert.Empty(GetApiDescription((HttpRequest request) => { }).ParameterDescriptions); Assert.Empty(GetApiDescription((HttpResponse response) => { }).ParameterDescriptions); @@ -443,8 +461,8 @@ static void AssertBodyParameter(ApiDescription apiDescription, string expectedNa Assert.Equal(BindingSource.Body, param.Source); } - AssertBodyParameter(GetApiDescription((InferredJsonClass foo) => { }), "foo", typeof(InferredJsonClass)); - AssertBodyParameter(GetApiDescription(([FromBody] int bar) => { }), "bar", typeof(int)); + AssertBodyParameter(GetApiDescription((InferredJsonClass foo) => { }, httpMethods: ["POST"]), "foo", typeof(InferredJsonClass)); + AssertBodyParameter(GetApiDescription(([FromBody] int bar) => { }, httpMethods: ["POST"]), "bar", typeof(int)); } [Fact] @@ -459,7 +477,7 @@ public void AddsDefaultValueFromParameters() [Fact] public void AddsMultipleParameters() { - var apiDescription = GetApiDescription(([FromRoute] int foo, int bar, InferredJsonClass fromBody) => { }); + var apiDescription = GetApiDescription(([FromRoute] int foo, int bar, InferredJsonClass fromBody) => { }, httpMethods: ["POST"]); Assert.Equal(3, apiDescription.ParameterDescriptions.Count); var fooParam = apiDescription.ParameterDescriptions[0]; @@ -517,14 +535,14 @@ static void AssertParameters(ApiDescription apiDescription, string capturedName ); } - AssertParameters(GetApiDescription(([AsParameters] ArgumentListClass req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListClassWithReadOnlyProperties req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListStruct req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecord req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordStruct req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutPositionalParameters req) => { })); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{foo}"), "foo"); - AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{Foo}")); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListClass req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListClassWithReadOnlyProperties req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListStruct req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecord req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordStruct req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutPositionalParameters req) => { }, httpMethods: ["POST"])); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{foo}", httpMethods: ["POST"]), "foo"); + AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{Foo}", httpMethods: ["POST"])); } #nullable enable @@ -679,8 +697,8 @@ public void RespectsProducesProblemExtensionMethod() // Assert var apiDescription = Assert.Single(context.Results); - var responseTypes = Assert.Single(apiDescription.SupportedResponseTypes); - Assert.Equal(typeof(ProblemDetails), responseTypes.Type); + Assert.Contains(apiDescription.SupportedResponseTypes, m => m is { StatusCode: 400, Type: { } type } && type == typeof(ProblemDetails)); + Assert.Contains(apiDescription.SupportedResponseTypes, m => m is { StatusCode: 200, Type: { } type } && type == typeof(string)); } [Fact] @@ -1362,9 +1380,26 @@ private static IList GetApiDescriptions( var attributes = methodInfo.GetCustomAttributes(); var context = new ApiDescriptionProviderContext(Array.Empty()); - var httpMethodMetadata = new HttpMethodMetadata(httpMethods ?? new[] { "GET" }); - var metadataItems = new List(attributes) { methodInfo, httpMethodMetadata }; - var endpointMetadata = new EndpointMetadataCollection(metadataItems.ToArray()); + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(new InferredServiceClass()); + serviceCollection.AddKeyedSingleton("foo", new InferredServiceClass()); + var serviceProvider = serviceCollection.BuildServiceProvider(); + + var shouldDisableInferredBodyForMethod = httpMethods is null || httpMethods.Any(method => + // GET, DELETE, HEAD, CONNECT, TRACE, and OPTIONS normally do not contain bodies + method.Equals(HttpMethods.Get, StringComparison.Ordinal) || + method.Equals(HttpMethods.Delete, StringComparison.Ordinal) || + method.Equals(HttpMethods.Head, StringComparison.Ordinal) || + method.Equals(HttpMethods.Options, StringComparison.Ordinal) || + method.Equals(HttpMethods.Trace, StringComparison.Ordinal) || + method.Equals(HttpMethods.Connect, StringComparison.Ordinal)); + + var options = new RequestDelegateFactoryOptions { ServiceProvider = serviceProvider, DisableInferBodyFromParameters = shouldDisableInferredBodyForMethod }; + var requestDelegateResult = RequestDelegateFactory.Create(methodInfo, options: options); + + var httpMethodMetadata = new HttpMethodMetadata(httpMethods ?? ["GET"]); + var metadataItems = new List(requestDelegateResult.EndpointMetadata) { methodInfo, httpMethodMetadata }; + var endpointMetadata = new EndpointMetadataCollection([.. metadataItems, .. attributes]); var routePattern = RoutePatternFactory.Parse(pattern ?? "/"); var endpoint = new RouteEndpoint(httpContext => Task.CompletedTask, routePattern, 0, endpointMetadata, displayName); @@ -1387,7 +1422,7 @@ private static IList GetApiDescriptions( private static TestEndpointRouteBuilder CreateBuilder() => new TestEndpointRouteBuilder(new ApplicationBuilder(TestServiceProvider.Instance)); - private static ApiDescription GetApiDescription(Delegate action, string? pattern = null, string? displayName = null, IEnumerable? httpMethods = null) => + private static ApiDescription GetApiDescription(Delegate action, string? pattern = null, string? displayName = null, IEnumerable? httpMethods = null, RequestDelegateFactoryOptions? options = null) => Assert.Single(GetApiDescriptions(action, pattern, displayName: displayName, httpMethods: httpMethods)); private static void TestAction() @@ -1410,6 +1445,10 @@ private interface IInferredJsonInterface { } + private class InferredServiceClass : IInferredServiceInterface + { + } + private class ServiceProviderIsService : IServiceProviderIsService { public bool IsService(Type serviceType) => serviceType == typeof(IInferredServiceInterface); From 6dda0f28bd028a9a63160270ecb44b8c9c6a7633 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 16 Jul 2024 12:22:53 -0700 Subject: [PATCH 02/17] Enable trimming checks for Mvc.Core and consumers --- .../src/DefaultApiDescriptionProvider.cs | 4 +- .../MvcApiExplorerMvcCoreBuilderExtensions.cs | 3 + ...icrosoft.AspNetCore.Mvc.ApiExplorer.csproj | 2 +- ...spNetCore.Mvc.Core.WarningSuppressions.xml | 665 ++++++++++++++++++ .../src/Microsoft.AspNetCore.OpenApi.csproj | 2 +- .../LinkabilityChecker.csproj | 5 + 6 files changed, 678 insertions(+), 3 deletions(-) create mode 100644 src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml diff --git a/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs index 4aef98aabd96..531de27dbef6 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc.Abstractions; @@ -20,6 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer; /// Implements a provider of for actions represented /// by . /// +[RequiresUnreferencedCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] public class DefaultApiDescriptionProvider : IApiDescriptionProvider { private readonly MvcOptions _mvcOptions; @@ -268,7 +270,7 @@ parameter.ModelMetadata is DefaultModelMetadata defaultModelMetadata && !defaultModelMetadata.Attributes.Attributes.OfType().Any()) { // If we didn't see the parameter in the route and no FromRoute metadata is set, it probably means - // the parameter binding source was inferred (InferParameterBindingInfoConvention) + // the parameter binding source was inferred (InferParameterBindingInfoConvention) // probably because another route to this action contains it as route parameter and // will be removed from the API description // https://github.com/dotnet/aspnetcore/issues/26234 diff --git a/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs index 8e26708c6bc8..35ef2e0c3275 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -16,6 +17,7 @@ public static class MvcApiExplorerMvcCoreBuilderExtensions /// /// The . /// The . + [RequiresUnreferencedCode("MVC does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] public static IMvcCoreBuilder AddApiExplorer(this IMvcCoreBuilder builder) { ArgumentNullException.ThrowIfNull(builder); @@ -25,6 +27,7 @@ public static IMvcCoreBuilder AddApiExplorer(this IMvcCoreBuilder builder) } // Internal for testing. + [RequiresUnreferencedCode("MVC does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] internal static void AddApiExplorerServices(IServiceCollection services) { services.TryAddSingleton(); diff --git a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj index c097a4c40112..c5991d82ea44 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj +++ b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.csproj @@ -7,7 +7,7 @@ true aspnetcore;aspnetcoremvc false - true + true diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml new file mode 100644 index 000000000000..182bf4660827 --- /dev/null +++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml @@ -0,0 +1,665 @@ + + + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Builder.ControllerEndpointRouteBuilderExtensions.MapAreaControllerRoute(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder,System.String,System.String,System.String,System.Object,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Builder.ControllerEndpointRouteBuilderExtensions.MapControllerRoute(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder,System.String,System.String,System.Object,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Builder.MvcApplicationBuilderExtensions.<>c.<UseMvcWithDefaultRoute>b__1_0(Microsoft.AspNetCore.Routing.IRouteBuilder) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Builder.MvcAreaRouteBuilderExtensions.MapAreaRoute(Microsoft.AspNetCore.Routing.IRouteBuilder,System.String,System.String,System.String,System.Object,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.AcceptedAtActionResult.#ctor(System.String,System.String,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.AcceptedAtRouteResult.#ctor(System.String,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.OnProvidersExecuting(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelProviderContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ApplicationParts.AssemblyPart.get_Types + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ApplicationParts.RelatedAssemblyAttribute.AssemblyLoadContextWrapper.LoadFromAssemblyPath(System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.CreatedAtActionResult.#ctor(System.String,System.String,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.CreatedAtRouteResult.#ctor(System.String,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.<ReadRequestBodyAsync>d__8.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.<WriteResponseBodyAsync>d__5.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvokerCache.GetCachedResult(Microsoft.AspNetCore.Mvc.ControllerContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.SystemTextJsonResultExecutor.<ExecuteAsync>d__4.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.JsonOptions.CreateDefaultTypeResolver + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder.#ctor(System.Type,Microsoft.Extensions.Logging.ILoggerFactory) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinder.#cctor + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.GetRecordTypeConstructor(System.Type,System.Reflection.ConstructorInfo[]) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreatePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity,Microsoft.Extensions.Internal.PropertyHelper) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertSimpleType(System.Object,System.Type,System.Globalization.CultureInfo) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterActionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor,Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Routing.RouteData) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterActionMethodImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.ActionContext,System.Collections.Generic.IReadOnlyDictionary{System.String,System.Object},System.Object,Microsoft.AspNetCore.Mvc.IActionResult) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterActionResultImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.ActionContext,Microsoft.AspNetCore.Mvc.IActionResult) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnActionExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnActionExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnActionExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnAuthorizationAsyncImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncAuthorizationFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnAuthorizationImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext,Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnExceptionAsyncImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncExceptionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnExceptionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Microsoft.AspNetCore.Mvc.Filters.IExceptionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResourceExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResourceExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResourceExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResultExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResultExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.AfterOnResultExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeActionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor,Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Routing.RouteData) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeActionMethodImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.ActionContext,System.Collections.Generic.IReadOnlyDictionary{System.String,System.Object},System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeActionResultImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.ActionContext,Microsoft.AspNetCore.Mvc.IActionResult) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnActionExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnActionExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnActionExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnAuthorizationAsyncImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncAuthorizationFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnAuthorizationImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext,Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnExceptionAsyncImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncExceptionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnExceptionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ExceptionContext,Microsoft.AspNetCore.Mvc.Filters.IExceptionFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResourceExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResourceExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResourceExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResourceExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncResourceFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResultExecutedImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutedContext,Microsoft.AspNetCore.Mvc.Filters.IResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResultExecutingImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.MvcCoreDiagnosticListenerExtensions.BeforeOnResultExecutionImpl(System.Diagnostics.DiagnosticListener,Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext,Microsoft.AspNetCore.Mvc.Filters.IAsyncResultFilter) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.RedirectToActionResult.#ctor(System.String,System.String,System.Object,System.Boolean,System.Boolean,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.RedirectToPageResult.#ctor(System.String,System.String,System.Object,System.Boolean,System.Boolean,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.RedirectToRouteResult.#ctor(System.String,System.Object,System.Boolean,System.Boolean,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.ActionEndpointFactory.AddActionDataToBuilder(Microsoft.AspNetCore.Builder.EndpointBuilder,System.Collections.Generic.HashSet{System.String},Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor,System.String,Microsoft.AspNetCore.Routing.RouteValueDictionary,System.Boolean,System.Boolean,System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}}) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.ActionEndpointFactory.AddEndpoints(System.Collections.Generic.List{Microsoft.AspNetCore.Http.Endpoint},System.Collections.Generic.HashSet{System.String},Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor,System.Collections.Generic.IReadOnlyList{Microsoft.AspNetCore.Mvc.Routing.ConventionalRouteEntry},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.IReadOnlyList{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Boolean,Microsoft.AspNetCore.Routing.Patterns.RoutePattern) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.ConventionalRouteEntry.#ctor(System.String,System.String,Microsoft.AspNetCore.Routing.RouteValueDictionary,System.Collections.Generic.IDictionary{System.String,System.Object},Microsoft.AspNetCore.Routing.RouteValueDictionary,System.Int32,System.Collections.Generic.List{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}},System.Collections.Generic.List{System.Action{Microsoft.AspNetCore.Builder.EndpointBuilder}}) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper.RouteUrl(Microsoft.AspNetCore.Mvc.Routing.UrlRouteContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.Routing.UrlHelperBase.GetValuesDictionary(System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Page(Microsoft.AspNetCore.Mvc.IUrlHelper,System.String,System.String,System.Object,System.String,System.String,System.String) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Routing.ControllerLinkGeneratorExtensions.CreateAddress(Microsoft.AspNetCore.Http.HttpContext,System.String,System.String,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Routing.PageLinkGeneratorExtensions.CreateAddress(Microsoft.AspNetCore.Http.HttpContext,System.String,System.String,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.Extensions.DependencyInjection.MvcCoreRouteOptionsSetup.Configure(Microsoft.AspNetCore.Routing.RouteOptions) + + + ILLink + IL2026 + member + M:Microsoft.Extensions.Internal.PropertyActivator`1.#ctor(System.Reflection.PropertyInfo,System.Func{`0,System.Object}) + + + ILLink + IL2057 + member + M:Microsoft.AspNetCore.Mvc.ApplicationParts.ProvideApplicationPartFactoryAttribute.GetFactoryType + + + ILLink + IL2060 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.TryGetReader(System.Type,System.Func{System.Object,System.Threading.CancellationToken,System.Threading.Tasks.Task{System.Collections.ICollection}}@) + + + ILLink + IL2060 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.PropertyValueSetter.SetValue(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,System.Object,System.Object) + + + ILLink + IL2060 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.DefaultCollectionValidationStrategy.<>c.<GetEnumeratorForElementType>b__5_0(System.Type) + + + ILLink + IL2062 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2062 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2062 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreatePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2062 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2062 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.GetCompatibleCollection``1(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext,System.Nullable{System.Int32}) + + + ILLink + IL2062 + type + T:Microsoft.Extensions.Internal.CopyOnWriteDictionary`2 + + + ILLink + IL2065 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2065 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterConfigurationProvider.CreateConfigureDelegate(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.Formatters.InputFormatter.GetDefaultValueForType(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.TypeActivatorCache.<>c.<#ctor>b__3_0(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BinderTypeModelBinder.#ctor(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1.CreateInstance(System.Type) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder.#ctor(System.Type,Microsoft.Extensions.Logging.ILoggerFactory) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.GetRecordTypeConstructor(System.Type,System.Reflection.ConstructorInfo[]) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertSimpleType(System.Object,System.Type,System.Globalization.CultureInfo) + + + ILLink + IL2067 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertTo(System.Object,System.Type,System.Globalization.CultureInfo) + + + ILLink + IL2067 + member + M:Microsoft.Extensions.Internal.PropertyActivator`1.GetActivatableProperties(System.Type,System.Type,System.Boolean) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.ApiConventionMethodAttribute.GetConventionMethod(System.Type,System.String) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.ApiConventionTypeAttribute.EnsureValid(System.Type) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterConfigurationProvider.FindMethod(System.Type,System.Type) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterConfigurationProvider.HasParameterlessConstructor(System.Type) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.<GetRecordTypeConstructor>g__IsRecordType|2_0(System.Type) + + + ILLink + IL2070 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.GetBoundConstructor(System.Type) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.OnProvidersExecuting(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelProviderContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartFactory.GetApplicationPartFactory(System.Reflection.Assembly) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.CreateActivator(Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.Infrastructure.ParameterDefaultValues.GetParameterDefaultValue(System.Reflection.ParameterInfo) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.BuildFactoryExpression(System.Reflection.ConstructorInfo,System.Linq.Expressions.Expression) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelAttributes.GetAttributesForProperty(System.Type,System.Reflection.PropertyInfo,System.Type) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertSimpleType(System.Object,System.Type,System.Globalization.CultureInfo) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.TypeFilterAttribute.CreateInstance(System.IServiceProvider) + + + ILLink + IL2072 + member + M:Microsoft.Extensions.DependencyInjection.MvcCoreMvcBuilderExtensions.AddControllersAsServices(Microsoft.Extensions.DependencyInjection.IMvcBuilder) + + + ILLink + IL2072 + member + M:Microsoft.Extensions.DependencyInjection.MvcCoreMvcCoreBuilderExtensions.AddControllersAsServices(Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder) + + + ILLink + IL2075 + member + M:Microsoft.AspNetCore.Mvc.ApiExplorer.ApiConventionResult.GetConventionMethod(System.Reflection.MethodInfo,Microsoft.AspNetCore.Mvc.ApiConventionTypeAttribute[]) + + + ILLink + IL2075 + member + M:Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.OnProvidersExecuting(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelProviderContext) + + + ILLink + IL2075 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinder.#cctor + + + ILLink + IL2103 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.GetPredicateExpression``1(System.Linq.Expressions.Expression{System.Func{``0,System.Object}}) + + + diff --git a/src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj b/src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj index 496c4efabb3e..65688d2eb269 100644 --- a/src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj +++ b/src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj @@ -7,7 +7,7 @@ true Provides APIs for annotating route handler endpoints in ASP.NET Core with OpenAPI annotations. - true + true true $(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated diff --git a/src/Tools/LinkabilityChecker/LinkabilityChecker.csproj b/src/Tools/LinkabilityChecker/LinkabilityChecker.csproj index 6656919a79c8..3c3a981cac85 100644 --- a/src/Tools/LinkabilityChecker/LinkabilityChecker.csproj +++ b/src/Tools/LinkabilityChecker/LinkabilityChecker.csproj @@ -10,6 +10,11 @@ + + From d5bf4e64810a45a3c2ef687ee8d06e28c7cd9ae4 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 16 Jul 2024 15:46:57 -0700 Subject: [PATCH 03/17] Add setup for NativeAoT test project for OpenAPI --- eng/RequiresDelayedBuildProjects.props | 1 + .../linker/SupportFiles/Directory.Build.targets | 2 ++ eng/testing/linker/project.csproj.template | 3 ++- eng/testing/linker/trimmingTests.targets | 1 + src/OpenApi/OpenApi.slnf | 5 ++++- ...ft.AspNetCore.OpenApi.Microbenchmarks.csproj | 2 +- src/OpenApi/sample/Sample.csproj | 4 ++-- .../BasicMinimalApiWithOpenApiDependency.cs | 17 +++++++++++++++++ ...osoft.AspNetCore.OpenApi.NativeAotTests.proj | 10 ++++++++++ .../Comparers/OpenApiAnyComparerTests.cs | 0 .../OpenApiDiscriminatorComparerTests.cs | 0 .../OpenApiExternalDocsComparerTests.cs | 0 .../Comparers/OpenApiReferenceComparerTests.cs | 0 .../Comparers/OpenApiSchemaComparerTests.cs | 0 .../Comparers/OpenApiXmlComparerTests.cs | 0 .../Extensions/ApiDescriptionExtensionsTests.cs | 0 .../Extensions/JsonTypeInfoExtensionsTests.cs | 0 ...penApiEndpointRouteBuilderExtensionsTests.cs | 0 .../OpenApiRouteHandlerBuilderExtensionTests.cs | 0 .../OpenApiServiceCollectionExtensionsTests.cs | 0 .../OpenApiDocumentIntegrationTests.cs | 0 .../Integration/SampleAppFixture.cs | 0 ...cument_documentName=controllers.verified.txt | 0 ...nApiDocument_documentName=forms.verified.txt | 0 ...Document_documentName=responses.verified.txt | 0 ...ent_documentName=schemas-by-ref.verified.txt | 0 ...OpenApiDocument_documentName=v1.verified.txt | 0 ...OpenApiDocument_documentName=v2.verified.txt | 0 .../Microsoft.AspNetCore.OpenApi.Tests.csproj | 0 .../Services/CreateSchemaReferenceIdTests.cs | 0 .../Services/OpenApiDocumentProviderTests.cs | 0 .../OpenApiDocumentServiceTests.Info.cs | 0 .../OpenApiDocumentServiceTests.Operations.cs | 0 .../OpenApiDocumentServiceTests.Parameters.cs | 0 .../OpenApiDocumentServiceTests.Paths.cs | 0 .../OpenApiDocumentServiceTests.RequestBody.cs | 0 .../OpenApiDocumentServiceTests.Responses.cs | 0 .../OpenApiDocumentServiceTests.Servers.cs | 0 .../Services/OpenApiDocumentServiceTestsBase.cs | 0 .../Services/OpenApiGeneratorTests.cs | 0 .../OpenApiSchemaService.ParameterSchemas.cs | 0 .../OpenApiSchemaService.PolymorphicSchemas.cs | 0 .../OpenApiSchemaService.RequestBodySchemas.cs | 0 .../OpenApiSchemaService.ResponseSchemas.cs | 0 .../Shared/SharedTypes.Polymorphism.cs | 0 .../Shared/SharedTypes.cs | 0 .../Transformers/DocumentTransformerTests.cs | 0 .../OpenApiSchemaReferenceTransformerTests.cs | 0 .../Transformers/OpenApiOptionsTests.cs | 0 .../Transformers/OperationTransformerTests.cs | 0 .../Transformers/SchemaTransformerTests.cs | 0 51 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/BasicMinimalApiWithOpenApiDependency.cs create mode 100644 src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Comparers/OpenApiAnyComparerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Comparers/OpenApiDiscriminatorComparerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Comparers/OpenApiExternalDocsComparerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Comparers/OpenApiReferenceComparerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Comparers/OpenApiSchemaComparerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Comparers/OpenApiXmlComparerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Extensions/ApiDescriptionExtensionsTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Extensions/JsonTypeInfoExtensionsTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Extensions/OpenApiServiceCollectionExtensionsTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Integration/OpenApiDocumentIntegrationTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Integration/SampleAppFixture.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Microsoft.AspNetCore.OpenApi.Tests.csproj (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/CreateSchemaReferenceIdTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentProviderTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiDocumentServiceTestsBase.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiGeneratorTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Shared/SharedTypes.Polymorphism.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Shared/SharedTypes.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Transformers/DocumentTransformerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Transformers/OpenApiOptionsTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Transformers/OperationTransformerTests.cs (100%) rename src/OpenApi/test/{ => Microsoft.AspNetCore.OpenApi.Tests}/Transformers/SchemaTransformerTests.cs (100%) diff --git a/eng/RequiresDelayedBuildProjects.props b/eng/RequiresDelayedBuildProjects.props index 3c1626ba53bf..1a771e0d5e74 100644 --- a/eng/RequiresDelayedBuildProjects.props +++ b/eng/RequiresDelayedBuildProjects.props @@ -20,6 +20,7 @@ + diff --git a/eng/testing/linker/SupportFiles/Directory.Build.targets b/eng/testing/linker/SupportFiles/Directory.Build.targets index 8d7674b8923a..0b4c65ac90c2 100644 --- a/eng/testing/linker/SupportFiles/Directory.Build.targets +++ b/eng/testing/linker/SupportFiles/Directory.Build.targets @@ -75,6 +75,8 @@ + + diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 8efde8d4b648..a3351945d111 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -11,6 +11,7 @@ 99.9 <_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) + $(InterceptorsPreviewNamespaces);{InterceptorsPreviewNamespacesArgs} {AdditionalProperties} @@ -21,5 +22,5 @@ {AdditionalProjectReferences} - + diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets index 1018b14a641b..8d569ba207d7 100644 --- a/eng/testing/linker/trimmingTests.targets +++ b/eng/testing/linker/trimmingTests.targets @@ -91,6 +91,7 @@ .Replace('{AdditionalProperties}', '$(_additionalPropertiesString)') .Replace('{RuntimeHostConfigurationOptions}', '$(_runtimeHostConfigurationOptionsString)') .Replace('{AdditionalProjectReferences}', '$(_additionalProjectReferencesString)') + .Replace('{InterceptorsPreviewNamespacesArgs}', '%(TestConsoleApps.InterceptorNamespaces)') )" Overwrite="true" /> - + diff --git a/src/OpenApi/sample/Sample.csproj b/src/OpenApi/sample/Sample.csproj index aa85248ddd31..31a0291457cc 100644 --- a/src/OpenApi/sample/Sample.csproj +++ b/src/OpenApi/sample/Sample.csproj @@ -22,8 +22,8 @@ - - + + diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/BasicMinimalApiWithOpenApiDependency.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/BasicMinimalApiWithOpenApiDependency.cs new file mode 100644 index 000000000000..01fe9786c830 --- /dev/null +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/BasicMinimalApiWithOpenApiDependency.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateSlimBuilder(); + +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +app.MapOpenApi(); + +app.MapGet("/", () => "Hello World!"); +app.MapGet("/{name}", (string name) => $"Hello {name}!"); + +return 100; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj new file mode 100644 index 000000000000..379101c03b18 --- /dev/null +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj @@ -0,0 +1,10 @@ + + + + <_TestConsoleAppSourceFiles Include="BasicMinimalApiWithOpenApiDependency.cs"> + EnableRequestDelegateGenerator + Microsoft.AspNetCore.Http.Generated + + + + diff --git a/src/OpenApi/test/Comparers/OpenApiAnyComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiAnyComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiAnyComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiAnyComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiDiscriminatorComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiDiscriminatorComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiDiscriminatorComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiDiscriminatorComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiExternalDocsComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiExternalDocsComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiExternalDocsComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiExternalDocsComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiReferenceComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiReferenceComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiReferenceComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiReferenceComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiSchemaComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiSchemaComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiSchemaComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiSchemaComparerTests.cs diff --git a/src/OpenApi/test/Comparers/OpenApiXmlComparerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiXmlComparerTests.cs similarity index 100% rename from src/OpenApi/test/Comparers/OpenApiXmlComparerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Comparers/OpenApiXmlComparerTests.cs diff --git a/src/OpenApi/test/Extensions/ApiDescriptionExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/ApiDescriptionExtensionsTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/ApiDescriptionExtensionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/ApiDescriptionExtensionsTests.cs diff --git a/src/OpenApi/test/Extensions/JsonTypeInfoExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/JsonTypeInfoExtensionsTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/JsonTypeInfoExtensionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/JsonTypeInfoExtensionsTests.cs diff --git a/src/OpenApi/test/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs diff --git a/src/OpenApi/test/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs diff --git a/src/OpenApi/test/Extensions/OpenApiServiceCollectionExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiServiceCollectionExtensionsTests.cs similarity index 100% rename from src/OpenApi/test/Extensions/OpenApiServiceCollectionExtensionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiServiceCollectionExtensionsTests.cs diff --git a/src/OpenApi/test/Integration/OpenApiDocumentIntegrationTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/OpenApiDocumentIntegrationTests.cs similarity index 100% rename from src/OpenApi/test/Integration/OpenApiDocumentIntegrationTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/OpenApiDocumentIntegrationTests.cs diff --git a/src/OpenApi/test/Integration/SampleAppFixture.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/SampleAppFixture.cs similarity index 100% rename from src/OpenApi/test/Integration/SampleAppFixture.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/SampleAppFixture.cs diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=controllers.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=forms.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=responses.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=schemas-by-ref.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v1.verified.txt diff --git a/src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt similarity index 100% rename from src/OpenApi/test/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=v2.verified.txt diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests.csproj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Microsoft.AspNetCore.OpenApi.Tests.csproj similarity index 100% rename from src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests.csproj rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Microsoft.AspNetCore.OpenApi.Tests.csproj diff --git a/src/OpenApi/test/Services/CreateSchemaReferenceIdTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs similarity index 100% rename from src/OpenApi/test/Services/CreateSchemaReferenceIdTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentProviderTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentProviderTests.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentProviderTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentProviderTests.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Info.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Servers.cs diff --git a/src/OpenApi/test/Services/OpenApiDocumentServiceTestsBase.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentServiceTestsBase.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiDocumentServiceTestsBase.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentServiceTestsBase.cs diff --git a/src/OpenApi/test/Services/OpenApiGeneratorTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiGeneratorTests.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiGeneratorTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiGeneratorTests.cs diff --git a/src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs diff --git a/src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs diff --git a/src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs diff --git a/src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs similarity index 100% rename from src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs diff --git a/src/OpenApi/test/Shared/SharedTypes.Polymorphism.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.Polymorphism.cs similarity index 100% rename from src/OpenApi/test/Shared/SharedTypes.Polymorphism.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.Polymorphism.cs diff --git a/src/OpenApi/test/Shared/SharedTypes.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.cs similarity index 100% rename from src/OpenApi/test/Shared/SharedTypes.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.cs diff --git a/src/OpenApi/test/Transformers/DocumentTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/DocumentTransformerTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/DocumentTransformerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/DocumentTransformerTests.cs diff --git a/src/OpenApi/test/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs diff --git a/src/OpenApi/test/Transformers/OpenApiOptionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/OpenApiOptionsTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/OpenApiOptionsTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/OpenApiOptionsTests.cs diff --git a/src/OpenApi/test/Transformers/OperationTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/OperationTransformerTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/OperationTransformerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/OperationTransformerTests.cs diff --git a/src/OpenApi/test/Transformers/SchemaTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs similarity index 100% rename from src/OpenApi/test/Transformers/SchemaTransformerTests.cs rename to src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs From 30d4b6c639d85c61e97865dc7392c513f72dd4a7 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 16 Jul 2024 16:27:24 -0700 Subject: [PATCH 04/17] Update AspNetCore.sln reference to OpenAPI tests --- AspNetCore.sln | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/AspNetCore.sln b/AspNetCore.sln index a9c2857c4182..aba0325d0351 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1674,8 +1674,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResultsOfTGenerator", "src\ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OpenApi", "OpenApi", "{2299CCD8-8F9C-4F2B-A633-9BF4DA81022B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OpenApi.Tests", "src\OpenApi\test\Microsoft.AspNetCore.OpenApi.Tests.csproj", "{3AEFB466-6310-4F3F-923F-9154224E3629}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OpenApi", "src\OpenApi\src\Microsoft.AspNetCore.OpenApi.csproj", "{EFC8EA45-572D-4D8D-A597-9045A2D8EC40}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.RateLimiting", "src\Middleware\RateLimiting\src\Microsoft.AspNetCore.RateLimiting.csproj", "{8EE73488-2B92-42BD-96C9-0DD65405C828}" @@ -1818,6 +1816,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GetDocumentSample", "src\To EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorUnitedApp.Client", "src\Components\Samples\BlazorUnitedApp.Client\BlazorUnitedApp.Client.csproj", "{757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B32FF7A7-9CB3-4DCD-AE97-3B2594DB9DAC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.OpenApi.Tests", "src\OpenApi\test\Microsoft.AspNetCore.OpenApi.Tests\Microsoft.AspNetCore.OpenApi.Tests.csproj", "{B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -10147,22 +10149,6 @@ Global {9716D0D0-2251-44DD-8596-67D253EEF41C}.Release|x64.Build.0 = Release|Any CPU {9716D0D0-2251-44DD-8596-67D253EEF41C}.Release|x86.ActiveCfg = Release|Any CPU {9716D0D0-2251-44DD-8596-67D253EEF41C}.Release|x86.Build.0 = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|arm64.ActiveCfg = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|arm64.Build.0 = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|x64.ActiveCfg = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|x64.Build.0 = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|x86.ActiveCfg = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Debug|x86.Build.0 = Debug|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|Any CPU.Build.0 = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|arm64.ActiveCfg = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|arm64.Build.0 = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|x64.ActiveCfg = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|x64.Build.0 = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|x86.ActiveCfg = Release|Any CPU - {3AEFB466-6310-4F3F-923F-9154224E3629}.Release|x86.Build.0 = Release|Any CPU {EFC8EA45-572D-4D8D-A597-9045A2D8EC40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EFC8EA45-572D-4D8D-A597-9045A2D8EC40}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFC8EA45-572D-4D8D-A597-9045A2D8EC40}.Debug|arm64.ActiveCfg = Debug|Any CPU @@ -10995,6 +10981,22 @@ Global {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x64.Build.0 = Release|Any CPU {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x86.ActiveCfg = Release|Any CPU {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63}.Release|x86.Build.0 = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|arm64.ActiveCfg = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|arm64.Build.0 = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|x64.Build.0 = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Debug|x86.Build.0 = Debug|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|Any CPU.Build.0 = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|arm64.ActiveCfg = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|arm64.Build.0 = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|x64.ActiveCfg = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|x64.Build.0 = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|x86.ActiveCfg = Release|Any CPU + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -11824,7 +11826,6 @@ Global {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7} = {AA5ABFBC-177C-421E-B743-005E0FD1248B} {9716D0D0-2251-44DD-8596-67D253EEF41C} = {323C3EB6-1D15-4B3D-918D-699D7F64DED9} {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} = {017429CC-C5FB-48B4-9C46-034E29EE2F06} - {3AEFB466-6310-4F3F-923F-9154224E3629} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} {EFC8EA45-572D-4D8D-A597-9045A2D8EC40} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} {8EE73488-2B92-42BD-96C9-0DD65405C828} = {1D865E78-7A66-4CA9-92EE-2B350E45281F} {41FF4F96-98D2-4482-A2A7-4B179E80D285} = {1D865E78-7A66-4CA9-92EE-2B350E45281F} @@ -11893,6 +11894,8 @@ Global {6A19D94D-2BC6-4198-BE2E-342688FDBA4B} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0} {D8F7091E-A2D1-4E81-BA7C-97EAE392D683} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0} {757CBDE0-5D0A-4FD8-99F3-6C20BDDD4E63} = {5FE1FBC1-8CE3-4355-9866-44FE1307C5F1} + {B32FF7A7-9CB3-4DCD-AE97-3B2594DB9DAC} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} + {B9BBC1A8-7F58-4F43-94C3-5F3CB125CEF7} = {B32FF7A7-9CB3-4DCD-AE97-3B2594DB9DAC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} From 9bea9bc960548d08f6a7e1684fd7b52b94e267e6 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 16 Jul 2024 16:45:29 -0700 Subject: [PATCH 05/17] Fix OpenApi.slnf --- src/OpenApi/OpenApi.slnf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenApi/OpenApi.slnf b/src/OpenApi/OpenApi.slnf index f618c0a08863..5bfe804cccdd 100644 --- a/src/OpenApi/OpenApi.slnf +++ b/src/OpenApi/OpenApi.slnf @@ -13,7 +13,7 @@ "src\\OpenApi\\perf\\Microbenchmarks\\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj", "src\\OpenApi\\sample\\Sample.csproj", "src\\OpenApi\\src\\Microsoft.AspNetCore.OpenApi.csproj", - "src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj" + "src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj", "src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.Tests\\Microsoft.AspNetCore.OpenApi.Tests.csproj", "src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.NativeTests\\Microsoft.AspNetCore.OpenApi.NativeAoTests.proj", "src\\OpenApi\\sample\\Sample.csproj", From 9c7d11eed340719b25794224fa4d4520eb032ca2 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 16 Jul 2024 17:54:50 -0700 Subject: [PATCH 06/17] Address feedback from API review --- .../src/Metadata/IParameterBindingMetadata.cs | 4 +-- .../gen/RequestDelegateGeneratorSources.cs | 31 ++++++++++++------- .../StaticRouteHandlerModel.Emitter.cs | 2 +- .../src/RequestDelegateFactory.cs | 4 +++ ...ion_BindAsync_NullableReturn.generated.txt | 31 ++++++++++++------- ...MapAction_BindAsync_Snapshot.generated.txt | 31 ++++++++++++------- ...Param_ComplexReturn_Snapshot.generated.txt | 31 ++++++++++++------- ...Header_ComplexTypeArrayParam.generated.txt | 31 ++++++++++++------- ...der_NullableStringArrayParam.generated.txt | 31 ++++++++++++------- ...licitHeader_StringArrayParam.generated.txt | 31 ++++++++++++------- ...tQuery_ComplexTypeArrayParam.generated.txt | 31 ++++++++++++------- ...ery_NullableStringArrayParam.generated.txt | 31 ++++++++++++------- ...plicitQuery_StringArrayParam.generated.txt | 31 ++++++++++++------- ...eParam_SimpleReturn_Snapshot.generated.txt | 31 ++++++++++++------- ...Source_SimpleReturn_Snapshot.generated.txt | 31 ++++++++++++------- ...tQuery_ComplexTypeArrayParam.generated.txt | 31 ++++++++++++------- ...ery_NullableStringArrayParam.generated.txt | 31 ++++++++++++------- ...gArrayParam_EmptyQueryValues.generated.txt | 31 ++++++++++++------- ...ngArrayParam_QueryNotPresent.generated.txt | 31 ++++++++++++------- ...plicitQuery_StringArrayParam.generated.txt | 31 ++++++++++++------- ...ce_HandlesBothJsonAndService.generated.txt | 31 ++++++++++++------- ...pecialTypeParam_StringReturn.generated.txt | 31 ++++++++++++------- ...ipleStringParam_StringReturn.generated.txt | 31 ++++++++++++------- ...omplexTypeParam_StringReturn.generated.txt | 31 ++++++++++++------- ...SingleEnumParam_StringReturn.generated.txt | 31 ++++++++++++------- ...ngValueProvided_StringReturn.generated.txt | 31 ++++++++++++------- ...MetadataEmitter_Has_Metadata.generated.txt | 31 ++++++++++++------- ...AndBody_ShouldUseQueryString.generated.txt | 31 ++++++++++++------- ...AndBody_ShouldUseQueryString.generated.txt | 31 ++++++++++++------- ...String_AndBody_ShouldUseBody.generated.txt | 31 ++++++++++++------- ...String_AndBody_ShouldUseBody.generated.txt | 31 ++++++++++++------- ...String_AndBody_ShouldUseBody.generated.txt | 31 ++++++++++++------- ...hArrayQueryString_ShouldFail.generated.txt | 31 ++++++++++++------- ...tion_WithParams_StringReturn.generated.txt | 31 ++++++++++++------- ...ateValidateGeneratedFormCode.generated.txt | 31 ++++++++++++------- ...InterceptorsFromSameLocation.generated.txt | 31 ++++++++++++------- ...terceptorsFromDifferentFiles.generated.txt | 31 ++++++++++++------- .../VerifyAsParametersBaseline.generated.txt | 31 ++++++++++++------- .../RequestDelegateCreationTests.Metadata.cs | 4 +-- .../EndpointMetadataApiDescriptionProvider.cs | 3 +- 40 files changed, 711 insertions(+), 391 deletions(-) diff --git a/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs index 3c932c69f7e6..1ca1c6448ec2 100644 --- a/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs +++ b/src/Http/Http.Abstractions/src/Metadata/IParameterBindingMetadata.cs @@ -22,12 +22,12 @@ public interface IParameterBindingMetadata string Name { get; } /// - /// is the parameter is associated with a type that implements IParsable or exposes a TryParse method. + /// is the parameter is associated with a type that implements or exposes a TryParse method. /// bool HasTryParse { get; } /// - /// if the parameter is associated with a type that implements a BindAsync method. + /// if the parameter is associated with a type that implements a BindAsync method. /// bool HasBindAsync { get; } diff --git a/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs b/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs index a03eb4fa2417..d7c7a480446c 100644 --- a/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs +++ b/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs @@ -451,22 +451,31 @@ public override bool IsDefined(Type attributeType, bool inherit) public static string ParameterBindingMetadataClass = $$""" {{GeneratedCodeAttribute}} - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } """; diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs index 1bfed13b6c58..bd22c645025b 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs @@ -217,7 +217,7 @@ private static void EmitBuiltinResponseTypeMetadata(this Endpoint endpoint, Code } else if (response.IsAwaitable && response.ResponseType == null) { - codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(void), contentTypes: GeneratedMetadataConstants.JsonContentType));"); + codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(void), contentTypes: GeneratedMetadataConstants.PlaintextContentType));"); } else if (response.ResponseType is { } responseType) { diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 7d532d8110d8..b6a4aeeba7fb 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -1043,6 +1043,10 @@ private static void PopulateBuiltInResponseTypeMetadata(Type returnType, Endpoin { builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(type: typeof(string), statusCode: 200, PlaintextContentType)); } + else if (returnType == typeof(void)) + { + builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(returnType, statusCode: 200, PlaintextContentType)); + } else { builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(returnType, statusCode: 200, DefaultAcceptsAndProducesContentType)); diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt index b0899ef1300d..b17d8f132c15 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt @@ -371,22 +371,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt index bef9d0d17c4e..cd074b2550d4 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt @@ -2215,22 +2215,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt index 6bc90f525e6f..e0820c222878 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt @@ -420,22 +420,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt index 46eeaec7a821..9c14ef7fc2a3 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt @@ -268,22 +268,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt index c96684fe9251..338870404d4f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt @@ -240,22 +240,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt index faa3efdd5a16..5c7cdc3d5f31 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt @@ -240,22 +240,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt index 410901937b72..7be60ca31fb8 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt @@ -268,22 +268,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt index acfa3b8fb6b2..d1747211273c 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt @@ -239,22 +239,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt index 4db9f25b43e8..2a4903675562 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt @@ -239,22 +239,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt index 53ea673aa77a..0d43f3521412 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt @@ -446,22 +446,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt index 176c9b64d329..ec49a917a460 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt @@ -612,22 +612,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt index cfb6be2aa8fd..744cb1967877 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt @@ -275,22 +275,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt index 40551947460b..05f5de4967e7 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt @@ -336,22 +336,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt index 40551947460b..05f5de4967e7 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt @@ -336,22 +336,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt index 40551947460b..05f5de4967e7 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt @@ -336,22 +336,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt index 666539e56e44..fde7b721a577 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt @@ -336,22 +336,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt index 60edaff577a4..7866a45bf43a 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt @@ -354,22 +354,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt index 0183fb94ff17..ef673881c510 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt @@ -243,22 +243,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt index 6cc1065ecd72..6dcece0634d8 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt @@ -276,22 +276,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt index 5e1aebe69bd3..e11a4e1978b3 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt @@ -275,22 +275,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt index ee2722bd73ea..00ed02cdf437 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt @@ -275,22 +275,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt index 40ca9483edd8..9d8c1c5ec159 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt @@ -247,22 +247,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt index 83b537637c4d..cb8f45469fab 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt @@ -342,22 +342,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt index c94fd82bd797..1620b5412616 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt @@ -343,22 +343,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt index c94fd82bd797..1620b5412616 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt @@ -343,22 +343,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index c94fd82bd797..1620b5412616 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -343,22 +343,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index c94fd82bd797..1620b5412616 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -343,22 +343,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index 3c7f24d18680..7ebdfa4be28b 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -343,22 +343,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt index ab84fbb33777..8444c64d3afa 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt @@ -336,22 +336,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt index 2021b4a1de99..5c5b767c4993 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt @@ -435,22 +435,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt index d1524be100ef..bb30424cb7ff 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt @@ -393,22 +393,31 @@ file sealed class AntiforgeryMetadata : IAntiforgeryMetadata } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt index 8cd26408a415..c5ba9a893684 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt @@ -396,22 +396,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt index a6e3f4ad40a2..4c987b44f6b0 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt @@ -266,22 +266,31 @@ namespace Microsoft.AspNetCore.Http.Generated } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt index 5567f4d61e61..30b4541a6f49 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt @@ -1074,22 +1074,31 @@ namespace Microsoft.AspNetCore.Http.Generated public new bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% - file sealed class ParameterBindingMetadata( - string name, - ParameterInfo parameterInfo, - bool hasTryParse = false, - bool hasBindAsync = false, - bool isOptional = false) : Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata + file sealed class ParameterBindingMetadata: IParameterBindingMetadata { - public string Name => name; + internal ParameterBindingMetadata( + string name, + ParameterInfo parameterInfo, + bool hasTryParse = false, + bool hasBindAsync = false, + bool isOptional = false) + { + Name = name; + ParameterInfo = parameterInfo; + HasTryParse = hasTryParse; + HasBindAsync = hasBindAsync; + IsOptional = isOptional; + } + + public string Name { get; } - public bool HasTryParse => hasTryParse; + public bool HasTryParse { get; } - public bool HasBindAsync => hasBindAsync; + public bool HasBindAsync { get; } - public ParameterInfo ParameterInfo => parameterInfo; + public ParameterInfo ParameterInfo { get; } - public bool IsOptional => isOptional; + public bool IsOptional { get; } } %GENERATEDCODEATTRIBUTE% diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs index 8d89c6503a54..68f0af004651 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs @@ -92,7 +92,7 @@ public async Task MapAction_ReturnsTask_ProducesInferredMetadata() var metadata = endpoint.Metadata.OfType().Single(); Assert.Equal(200, metadata.StatusCode); - Assert.Equal("application/json", metadata.ContentTypes.Single()); + Assert.Equal("text/plain", metadata.ContentTypes.Single()); Assert.Equal(typeof(void), metadata.Type); } @@ -120,7 +120,7 @@ public async Task MapAction_ReturnsValueTask_ProducesInferredMetadata() var metadata = endpoint.Metadata.OfType().Single(); Assert.Equal(200, metadata.StatusCode); - Assert.Equal("application/json", metadata.ContentTypes.Single()); + Assert.Equal("text/plain", metadata.ContentTypes.Single()); Assert.Equal(typeof(void), metadata.Type); } diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 6e6de041d1ae..ee46c1e9f689 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -179,7 +179,8 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string return null; } - // Determine the "requiredness" based on nullability, default value or if allowEmpty is set + // Use the optionality status determined by the code generation layer which accounts for + // nullability, default values, and the whether or not `[FromBody(AllowEmpty = true)]`. var isOptional = parameter.IsOptional; var parameterDescriptor = CreateParameterDescriptor(parameter.ParameterInfo, pattern); var routeInfo = CreateParameterRouteInfo(pattern, parameter.ParameterInfo, isOptional); From 1e3e057c293edfb9a064cff1070072de040801bb Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 16 Jul 2024 18:17:39 -0700 Subject: [PATCH 07/17] Update OpenApi.slnf --- src/OpenApi/OpenApi.slnf | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OpenApi/OpenApi.slnf b/src/OpenApi/OpenApi.slnf index 5bfe804cccdd..3d616bfa4daf 100644 --- a/src/OpenApi/OpenApi.slnf +++ b/src/OpenApi/OpenApi.slnf @@ -15,7 +15,6 @@ "src\\OpenApi\\src\\Microsoft.AspNetCore.OpenApi.csproj", "src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj", "src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.Tests\\Microsoft.AspNetCore.OpenApi.Tests.csproj", - "src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.NativeTests\\Microsoft.AspNetCore.OpenApi.NativeAoTests.proj", "src\\OpenApi\\sample\\Sample.csproj", "src\\OpenApi\\perf\\Microbenchmarks\\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj" ] From 1592fc194802ee4734a5c84b551b428dc5adfbb5 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 16 Jul 2024 18:28:27 -0700 Subject: [PATCH 08/17] Confirming my sanity --- .../Microsoft.AspNetCore.OpenApi.NativeAotTests.proj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj index 379101c03b18..c2834663cdc1 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj @@ -1,10 +1,10 @@ - <_TestConsoleAppSourceFiles Include="BasicMinimalApiWithOpenApiDependency.cs"> + EnableRequestDelegateGenerator Microsoft.AspNetCore.Http.Generated - + From aa3ac70ee4d7d64e1707c9350a9590e75139afba Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Tue, 16 Jul 2024 21:12:16 -0700 Subject: [PATCH 09/17] Refactor ModelMetadata for trim compatability --- .../src/ModelBinding/ModelMetadata.cs | 71 +++++++++++++++++-- .../src/PublicAPI.Unshipped.txt | 1 + .../EndpointMetadataApiDescriptionProvider.cs | 7 +- .../src/EndpointModelMetadata.cs | 3 +- ...oft.AspNetCore.OpenApi.NativeAotTests.proj | 1 + 5 files changed, 74 insertions(+), 9 deletions(-) diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index 1f6e8042341a..b550ce006a6b 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -9,6 +9,7 @@ using System.Linq.Expressions; using System.Reflection; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; @@ -30,6 +31,17 @@ public abstract class ModelMetadata : IEquatable, IModelMetadata private static readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(throwOnInvalidMethod: false); + /// + /// Exposes a feature switch to disable generating model metadata with reflection-heavy strategies. + /// This is primarily intended for use in Minimal API-based scenarios where information is derived from + /// IParameterBindingMetadata + /// + [FeatureSwitchDefinition("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported")] + [FeatureGuard(typeof(RequiresDynamicCodeAttribute))] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] + private static bool IsEnhancedModelMetadataSupported { get; } = + AppContext.TryGetSwitch("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", out var isEnhancedModelMetadataSupported) ? isEnhancedModelMetadataSupported : true; + private int? _hashCode; private IReadOnlyList? _boundProperties; private IReadOnlyDictionary? _parameterMapping; @@ -44,8 +56,58 @@ private static readonly ParameterBindingMethodCache ParameterBindingMethodCache protected ModelMetadata(ModelMetadataIdentity identity) { Identity = identity; + if (IsEnhancedModelMetadataSupported) + { + InitializeTypeInformation(); + } + } + + /// + /// Creates a new from a instance + /// and its associated type. + /// + /// The associated with the generated. + /// The instance associated with the generated. + protected ModelMetadata(Type type, IParameterBindingMetadata? parameterBindingMetadata) + { + Identity = ModelMetadataIdentity.ForType(type); - InitializeTypeInformation(); + InitializeTypeInformationFromType(); + if (parameterBindingMetadata is not null) + { + InitializeTypeInformationFromParameterBindingMetadata(parameterBindingMetadata); + } + } + + private void InitializeTypeInformationFromType() + { + IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null; + IsReferenceOrNullableType = !ModelType.IsValueType || IsNullableValueType; + UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType; + + if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType)) + { + // Do nothing, not Enumerable. + } + else if (ModelType.IsArray) + { + IsEnumerableType = true; + ElementType = ModelType.GetElementType()!; + } + } + + private void InitializeTypeInformationFromParameterBindingMetadata(IParameterBindingMetadata parameterBindingMetadata) + { + // We assume that parameters bound from an endpoint's metadata originated from minimal API's source + // generation layer and are not convertible based on the `TypeConverter`s in MVC. + IsConvertibleType = false; + HasDefaultValue = parameterBindingMetadata.ParameterInfo.HasDefaultValue; + IsParseableType = parameterBindingMetadata.HasTryParse; + IsComplexType = !IsParseableType; + + var nullabilityContext = new NullabilityInfoContext(); + var nullability = nullabilityContext.Create(parameterBindingMetadata.ParameterInfo); + NullabilityState = nullability?.ReadState ?? NullabilityState.Unknown; } /// @@ -442,7 +504,7 @@ internal IReadOnlyDictionary BoundConstructorPrope /// from and without a TryParse method. Most POCO and types are therefore complex. /// Most, if not all, BCL value types are simple types. /// - public bool IsComplexType => !IsConvertibleType && !IsParseableType; + public bool IsComplexType { get; private set; } /// /// Gets a value indicating whether or not is a . @@ -647,12 +709,13 @@ public override int GetHashCode() return _hashCode.Value; } + [RequiresUnreferencedCode("Using ModelMetadata with 'Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported=true' is not trim compatible.")] + [RequiresDynamicCode("Using ModelMetadata with 'Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported=true' is not native AOT compatible.")] private void InitializeTypeInformation() { - Debug.Assert(ModelType != null); - IsConvertibleType = TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); IsParseableType = FindTryParseMethod(ModelType) is not null; + IsComplexType = !IsConvertibleType && !IsParseableType; IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null; IsReferenceOrNullableType = !ModelType.IsValueType || IsNullableValueType; UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType; diff --git a/src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..328c02871e6a 100644 --- a/src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.ModelMetadata(System.Type! type, Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata? parameterBindingMetadata) -> void diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index ee46c1e9f689..36b8bc4d0a43 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.Extensions.DependencyInjection; @@ -188,7 +187,7 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string return new ApiParameterDescription { Name = name, - ModelMetadata = CreateModelMetadata(paramType), + ModelMetadata = CreateModelMetadata(paramType, parameter), Source = source, DefaultValue = parameter.ParameterInfo.DefaultValue, Type = parameter.ParameterInfo.ParameterType, @@ -431,8 +430,8 @@ private static ApiResponseType CreateDefaultApiResponseType(Type responseType) } } - private static EndpointModelMetadata CreateModelMetadata(Type type) => - new(ModelMetadataIdentity.ForType(type)); + private static EndpointModelMetadata CreateModelMetadata(Type type, IParameterBindingMetadata? parameterBindingMetadata = null) => + new(type, parameterBindingMetadata); private static void AddResponseContentTypes(IList apiResponseFormats, IReadOnlyList contentTypes) { diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointModelMetadata.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointModelMetadata.cs index 3fd46f1798e2..95bbaaf92a62 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointModelMetadata.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointModelMetadata.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; +using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; @@ -10,7 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer; internal sealed class EndpointModelMetadata : ModelMetadata { - public EndpointModelMetadata(ModelMetadataIdentity identity) : base(identity) + public EndpointModelMetadata(Type type, IParameterBindingMetadata? parameterBindingMetadata) : base(type, parameterBindingMetadata) { IsBindingAllowed = true; } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj index c2834663cdc1..9d924fd3d640 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj @@ -4,6 +4,7 @@ EnableRequestDelegateGenerator Microsoft.AspNetCore.Http.Generated + Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported From 26dcc3b3106addf9c757be77dc5e728faf64c4e3 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 17 Jul 2024 07:45:37 -0700 Subject: [PATCH 10/17] Disable native AoT tests until Microsoft.OpenApi is trim-compatible --- .../Microsoft.AspNetCore.OpenApi.NativeAotTests.proj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj index 9d924fd3d640..2463d1bbb3af 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj @@ -1,11 +1,13 @@ - + + <_TestConsoleAppSourceFiles Include="BasicMinimalApiWithOpenApiDependency.cs"> EnableRequestDelegateGenerator Microsoft.AspNetCore.Http.Generated Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported - + From 6812d1df084f8d7fddc85c83d6b5ed0319993873 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 17 Jul 2024 15:13:25 -0700 Subject: [PATCH 11/17] Fix up NativeAoT tests for OpenApi --- eng/testing/linker/project.csproj.template | 13 +++++++++++++ ...Microsoft.AspNetCore.OpenApi.NativeAotTests.proj | 6 ++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index a3351945d111..b1c9b9094a8f 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -12,6 +12,10 @@ 99.9 <_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) $(InterceptorsPreviewNamespaces);{InterceptorsPreviewNamespacesArgs} + + false + + $(NoWarn);IL2104 {AdditionalProperties} @@ -23,4 +27,13 @@ {AdditionalProjectReferences} + + + + + + + + diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj index 2463d1bbb3af..9d924fd3d640 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj @@ -1,13 +1,11 @@ - - <_TestConsoleAppSourceFiles Include="BasicMinimalApiWithOpenApiDependency.cs"> + EnableRequestDelegateGenerator Microsoft.AspNetCore.Http.Generated Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported - + From fed4ca94833572b0a7a76c56172fa6e4f6b51844 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 18 Jul 2024 21:38:25 -0700 Subject: [PATCH 12/17] Fix ModelMetadata trimming without API changes --- .../src/ModelBinding/ModelMetadata.cs | 183 +++++++++--------- .../src/PublicAPI.Unshipped.txt | 1 - .../test/ModelBinding/ModelMetadataTest.cs | 35 ++++ .../EndpointMetadataApiDescriptionProvider.cs | 7 +- .../src/EndpointModelMetadata.cs | 3 +- .../src/Services/OpenApiDocumentService.cs | 19 +- 6 files changed, 150 insertions(+), 98 deletions(-) diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index b550ce006a6b..402b28be7e30 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -8,8 +8,8 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; @@ -31,17 +31,6 @@ public abstract class ModelMetadata : IEquatable, IModelMetadata private static readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(throwOnInvalidMethod: false); - /// - /// Exposes a feature switch to disable generating model metadata with reflection-heavy strategies. - /// This is primarily intended for use in Minimal API-based scenarios where information is derived from - /// IParameterBindingMetadata - /// - [FeatureSwitchDefinition("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported")] - [FeatureGuard(typeof(RequiresDynamicCodeAttribute))] - [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] - private static bool IsEnhancedModelMetadataSupported { get; } = - AppContext.TryGetSwitch("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", out var isEnhancedModelMetadataSupported) ? isEnhancedModelMetadataSupported : true; - private int? _hashCode; private IReadOnlyList? _boundProperties; private IReadOnlyDictionary? _parameterMapping; @@ -56,60 +45,14 @@ private static readonly ParameterBindingMethodCache ParameterBindingMethodCache protected ModelMetadata(ModelMetadataIdentity identity) { Identity = identity; - if (IsEnhancedModelMetadataSupported) - { - InitializeTypeInformation(); - } - } - /// - /// Creates a new from a instance - /// and its associated type. - /// - /// The associated with the generated. - /// The instance associated with the generated. - protected ModelMetadata(Type type, IParameterBindingMetadata? parameterBindingMetadata) - { - Identity = ModelMetadataIdentity.ForType(type); - - InitializeTypeInformationFromType(); - if (parameterBindingMetadata is not null) + InitializeTypeInformation(); + if (RuntimeFeature.IsDynamicCodeSupported) { - InitializeTypeInformationFromParameterBindingMetadata(parameterBindingMetadata); - } - } - - private void InitializeTypeInformationFromType() - { - IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null; - IsReferenceOrNullableType = !ModelType.IsValueType || IsNullableValueType; - UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType; - - if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType)) - { - // Do nothing, not Enumerable. - } - else if (ModelType.IsArray) - { - IsEnumerableType = true; - ElementType = ModelType.GetElementType()!; + InitializeDynamicTypeInformation(); } } - private void InitializeTypeInformationFromParameterBindingMetadata(IParameterBindingMetadata parameterBindingMetadata) - { - // We assume that parameters bound from an endpoint's metadata originated from minimal API's source - // generation layer and are not convertible based on the `TypeConverter`s in MVC. - IsConvertibleType = false; - HasDefaultValue = parameterBindingMetadata.ParameterInfo.HasDefaultValue; - IsParseableType = parameterBindingMetadata.HasTryParse; - IsComplexType = !IsParseableType; - - var nullabilityContext = new NullabilityInfoContext(); - var nullability = nullabilityContext.Create(parameterBindingMetadata.ParameterInfo); - NullabilityState = nullability?.ReadState ?? NullabilityState.Unknown; - } - /// /// Gets the type containing the property if this metadata is for a property; otherwise. /// @@ -490,11 +433,27 @@ internal IReadOnlyDictionary BoundConstructorPrope /// public abstract IReadOnlyList ValidatorMetadata { get; } + private Type? _elementType; + /// /// Gets the for elements of if that /// implements . /// - public Type? ElementType { get; private set; } + public Type? ElementType + { + get + { + if (!RuntimeFeature.IsDynamicCodeSupported) + { + throw new NotSupportedException("ElementType is not initialized in ModelMetadata in native AoT."); + } + return _elementType; + } + private set + { + _elementType = value; + } + } /// /// Gets a value indicating whether is a complex type. @@ -504,20 +463,36 @@ internal IReadOnlyDictionary BoundConstructorPrope /// from and without a TryParse method. Most POCO and types are therefore complex. /// Most, if not all, BCL value types are simple types. /// - public bool IsComplexType { get; private set; } + public bool IsComplexType => !IsConvertibleType && !IsParseableType; /// /// Gets a value indicating whether or not is a . /// public bool IsNullableValueType { get; private set; } + private bool? _isCollectionType; + /// /// Gets a value indicating whether or not is a collection type. /// /// /// A collection type is defined as a which is assignable to . /// - public bool IsCollectionType { get; private set; } + public bool IsCollectionType + { + get + { + if (_isCollectionType == null) + { + throw new NotSupportedException("IsCollectionType has not been initialized on this metadata instance."); + } + return _isCollectionType.Value; + } + private set + { + _isCollectionType = value; + } + } /// /// Gets a value indicating whether or not is an enumerable type. @@ -542,16 +517,49 @@ internal IReadOnlyDictionary BoundConstructorPrope /// public Type UnderlyingOrModelType { get; private set; } = default!; + private bool? _isParseableType; + /// /// Gets a value indicating whether or not has a TryParse method. /// - internal virtual bool IsParseableType { get; private set; } + internal virtual bool IsParseableType + { + get + { + if (!_isParseableType.HasValue) + { + throw new NotSupportedException("IsParseableType has not been initialized on this metadata instance."); + } + return _isParseableType.Value; + } + private set + { + _isParseableType = value; + } + } + + private bool? _isConvertibleType; /// /// Gets a value indicating whether or not has a /// from . /// - internal bool IsConvertibleType { get; private set; } + internal bool IsConvertibleType + { + get + { + if (!_isConvertibleType.HasValue) + { + throw new NotSupportedException("IsConvertibleType has not been initialized on this metadata instance."); + + } + return _isConvertibleType.Value; + } + private set + { + _isConvertibleType = value; + } + } /// /// Gets a value indicating the NullabilityState of the value or reference type. @@ -605,6 +613,8 @@ internal void ThrowIfRecordTypeHasValidationOnProperties() } } + [RequiresUnreferencedCode("Finding the TryParse method via reflection is not trim compatible.")] + [RequiresDynamicCode("Finding the TryParse method via reflection is not native AOT compatible.")] internal static Func? FindTryParseMethod(Type modelType) { if (modelType.IsByRef) @@ -709,21 +719,15 @@ public override int GetHashCode() return _hashCode.Value; } - [RequiresUnreferencedCode("Using ModelMetadata with 'Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported=true' is not trim compatible.")] - [RequiresDynamicCode("Using ModelMetadata with 'Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported=true' is not native AOT compatible.")] private void InitializeTypeInformation() { - IsConvertibleType = TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); - IsParseableType = FindTryParseMethod(ModelType) is not null; - IsComplexType = !IsConvertibleType && !IsParseableType; + Debug.Assert(ModelType != null); + IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null; IsReferenceOrNullableType = !ModelType.IsValueType || IsNullableValueType; UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType; HasDefaultValue = MetadataKind == ModelMetadataKind.Parameter && Identity.ParameterInfo!.HasDefaultValue; - var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection<>)); - IsCollectionType = collectionType != null; - var nullabilityContext = new NullabilityInfoContext(); var nullability = MetadataKind switch { @@ -733,27 +737,30 @@ private void InitializeTypeInformation() }; NullabilityState = nullability?.ReadState ?? NullabilityState.Unknown; - if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType)) - { - // Do nothing, not Enumerable. - } - else if (ModelType.IsArray) + if (ModelType.IsArray) { IsEnumerableType = true; ElementType = ModelType.GetElementType()!; } - else + } + + [RequiresUnreferencedCode("Using ModelMetadata with dynamic dependencies enabled is not trim compatible.")] + [RequiresDynamicCode("Using ModelMetadata with dynamic dependencies enabled is not native AOT compatible.")] + private void InitializeDynamicTypeInformation() + { + Debug.Assert(ModelType != null); + IsConvertibleType = TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); + IsParseableType = FindTryParseMethod(ModelType) is not null; + + var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection<>)); + _isCollectionType = collectionType != null; + + if (ModelType != typeof(string) && !ModelType.IsArray && typeof(IEnumerable).IsAssignableFrom(ModelType)) { IsEnumerableType = true; - var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(IEnumerable<>)); - ElementType = enumerableType?.GenericTypeArguments[0]!; - - if (ElementType == null) - { - // ModelType implements IEnumerable but not IEnumerable. - ElementType = typeof(object); - } + // Apply fallback when ModelType implements IEnumerable but not IEnumerable. + ElementType = enumerableType?.GenericTypeArguments[0] ?? typeof(object); Debug.Assert( ElementType != null, diff --git a/src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt index 328c02871e6a..7dc5c58110bf 100644 --- a/src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt @@ -1,2 +1 @@ #nullable enable -Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.ModelMetadata(System.Type! type, Microsoft.AspNetCore.Http.Metadata.IParameterBindingMetadata? parameterBindingMetadata) -> void diff --git a/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs b/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs index cc371e39a4ec..0d391f36352b 100644 --- a/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs +++ b/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs @@ -6,6 +6,7 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.DotNet.RemoteExecutor; namespace Microsoft.AspNetCore.Mvc.ModelBinding; @@ -406,6 +407,40 @@ public void GetMetadataForProperties_ByDefaultThrows_NotImplementedException() var result = Assert.Throws(() => metadata.GetMetadataForProperties(typeof(string))); } + [Fact] + public void DynamicPropertiesThrowWhenIsDynamicCodeSupportedIsTrue() + { + var options = new RemoteInvokeOptions(); + + options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", false); + using var remoteHandle = RemoteExecutor.Invoke(static () => + { + var metadata = new TestModelMetadata(typeof(DateTime)); + Assert.Throws(() => metadata.ElementType); + Assert.Throws(() => metadata.IsParseableType); + Assert.Throws(() => metadata.IsConvertibleType); + Assert.Throws(() => metadata.IsComplexType); + Assert.Throws(() => metadata.IsCollectionType); + }, options); + } + + [Fact] + public void DynamicPropertiesSetWhenIsDynamicCodeSupportedIsTrue() + { + var options = new RemoteInvokeOptions(); + + options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", true); + using var remoteHandle = RemoteExecutor.Invoke(static () => + { + var metadata = new TestModelMetadata(typeof(DateTime)); + Assert.Null(metadata.ElementType); + Assert.True(metadata.IsParseableType); + Assert.False(metadata.IsCollectionType); + Assert.True(metadata.IsConvertibleType); + Assert.False(metadata.IsComplexType); + }, options); + } + private class TestModelMetadata : ModelMetadata { private string _displayName; diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 36b8bc4d0a43..ee46c1e9f689 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.Extensions.DependencyInjection; @@ -187,7 +188,7 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string return new ApiParameterDescription { Name = name, - ModelMetadata = CreateModelMetadata(paramType, parameter), + ModelMetadata = CreateModelMetadata(paramType), Source = source, DefaultValue = parameter.ParameterInfo.DefaultValue, Type = parameter.ParameterInfo.ParameterType, @@ -430,8 +431,8 @@ private static ApiResponseType CreateDefaultApiResponseType(Type responseType) } } - private static EndpointModelMetadata CreateModelMetadata(Type type, IParameterBindingMetadata? parameterBindingMetadata = null) => - new(type, parameterBindingMetadata); + private static EndpointModelMetadata CreateModelMetadata(Type type) => + new(ModelMetadataIdentity.ForType(type)); private static void AddResponseContentTypes(IList apiResponseFormats, IReadOnlyList contentTypes) { diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointModelMetadata.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointModelMetadata.cs index 95bbaaf92a62..3fd46f1798e2 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointModelMetadata.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointModelMetadata.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; using System.Linq; -using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; @@ -11,7 +10,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer; internal sealed class EndpointModelMetadata : ModelMetadata { - public EndpointModelMetadata(Type type, IParameterBindingMetadata? parameterBindingMetadata) : base(type, parameterBindingMetadata) + public EndpointModelMetadata(ModelMetadataIdentity identity) : base(identity) { IsBindingAllowed = true; } diff --git a/src/OpenApi/src/Services/OpenApiDocumentService.cs b/src/OpenApi/src/Services/OpenApiDocumentService.cs index cc6c04d3610f..f3f9aba6c16f 100644 --- a/src/OpenApi/src/Services/OpenApiDocumentService.cs +++ b/src/OpenApi/src/Services/OpenApiDocumentService.cs @@ -362,12 +362,17 @@ private static bool IsRequired(ApiParameterDescription parameter) // in the same endpoint. if (description.TryGetFormParameters(out var formParameters)) { - return await GetFormRequestBody(description.SupportedRequestFormats, formParameters, cancellationToken); + var endpointMetadata = description.ActionDescriptor.EndpointMetadata; + return await GetFormRequestBody(description.SupportedRequestFormats, formParameters, endpointMetadata, cancellationToken); } return null; } - private async Task GetFormRequestBody(IList supportedRequestFormats, IEnumerable formParameters, CancellationToken cancellationToken) + private async Task GetFormRequestBody( + IList supportedRequestFormats, + IEnumerable formParameters, + IList endpointMetadata, + CancellationToken cancellationToken) { if (supportedRequestFormats.Count == 0) { @@ -425,11 +430,17 @@ private async Task GetFormRequestBody(IList() + .SingleOrDefault(parameter => parameter.Name == description.Name)? + .HasTryParse == false; if (hasMultipleFormParameters) { // Here and below: POCOs do not need to be need under their parameter name in the grouping. // The form-binding implementation will capture them implicitly. - if (description.ModelMetadata.IsComplexType) + if (isComplexType) { schema.AllOf.Add(parameterSchema); } @@ -447,7 +458,7 @@ private async Task GetFormRequestBody(IList Date: Fri, 19 Jul 2024 13:38:37 -0700 Subject: [PATCH 13/17] Update native AoT for MVC --- eng/TrimmableProjects.props | 2 + eng/testing/linker/project.csproj.template | 12 +- eng/testing/linker/trimmingTests.targets | 1 - ...e.Mvc.Abstractions.WarningSuppressions.xml | 12 ++ ...crosoft.AspNetCore.Mvc.Abstractions.csproj | 6 + .../src/ModelBinding/ModelMetadata.cs | 24 ++-- .../test/ModelBinding/ModelMetadataTest.cs | 4 +- ...re.Mvc.ApiExplorer.WarningSuppressions.xml | 11 ++ ...spNetCore.Mvc.Core.WarningSuppressions.xml | 110 ++++++++++-------- .../src/Microsoft.AspNetCore.Mvc.Core.csproj | 6 + ...oft.AspNetCore.OpenApi.NativeAotTests.proj | 1 - ...soft.AspNetCore.OpenApi.TrimmingTests.proj | 10 ++ .../LinkabilityChecker.csproj | 5 - 13 files changed, 136 insertions(+), 68 deletions(-) create mode 100644 src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml create mode 100644 src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.WarningSuppressions.xml create mode 100644 src/OpenApi/test/Microsoft.AspNetCore.OpenApi.TrimmingTests/Microsoft.AspNetCore.OpenApi.TrimmingTests.proj diff --git a/eng/TrimmableProjects.props b/eng/TrimmableProjects.props index 97e7a880d612..199d65af1b49 100644 --- a/eng/TrimmableProjects.props +++ b/eng/TrimmableProjects.props @@ -85,7 +85,9 @@ + + diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index b1c9b9094a8f..1affe37e05ef 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -11,7 +11,7 @@ 99.9 <_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) - $(InterceptorsPreviewNamespaces);{InterceptorsPreviewNamespacesArgs} + $(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated false @@ -27,13 +27,19 @@ {AdditionalProjectReferences} + - - + + + true + true + + diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets index 8d569ba207d7..1018b14a641b 100644 --- a/eng/testing/linker/trimmingTests.targets +++ b/eng/testing/linker/trimmingTests.targets @@ -91,7 +91,6 @@ .Replace('{AdditionalProperties}', '$(_additionalPropertiesString)') .Replace('{RuntimeHostConfigurationOptions}', '$(_runtimeHostConfigurationOptionsString)') .Replace('{AdditionalProjectReferences}', '$(_additionalProjectReferencesString)') - .Replace('{InterceptorsPreviewNamespacesArgs}', '%(TestConsoleApps.InterceptorNamespaces)') )" Overwrite="true" /> + + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.#ctor(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + This warning is left in the product so developers get an ILLink warning when trimming an app when Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported=true. + + + diff --git a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj index 376dd41c133b..16a5411f1caf 100644 --- a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj +++ b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.csproj @@ -9,6 +9,12 @@ Microsoft.AspNetCore.Mvc.IActionResult true aspnetcore;aspnetcoremvc false + + true + false + false diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index 402b28be7e30..f7135f27261f 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; @@ -28,8 +27,18 @@ public abstract class ModelMetadata : IEquatable, IModelMetadata /// public static readonly int DefaultOrder = 10000; - private static readonly ParameterBindingMethodCache ParameterBindingMethodCache - = new(throwOnInvalidMethod: false); + /// + /// Exposes a feature switch to disable generating model metadata with reflection-heavy strategies. + /// This is primarily intended for use in Minimal API-based scenarios where information is derived from + /// IParameterBindingMetadata + /// + [FeatureSwitchDefinition("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported")] + [FeatureGuard(typeof(RequiresDynamicCodeAttribute))] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] + private static bool IsEnhancedModelMetadataSupported { get; } = + AppContext.TryGetSwitch("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", out var isEnhancedModelMetadataSupported) ? isEnhancedModelMetadataSupported : true; + + private static ParameterBindingMethodCache? ParameterBindingMethodCache; private int? _hashCode; private IReadOnlyList? _boundProperties; @@ -47,7 +56,7 @@ protected ModelMetadata(ModelMetadataIdentity identity) Identity = identity; InitializeTypeInformation(); - if (RuntimeFeature.IsDynamicCodeSupported) + if (IsEnhancedModelMetadataSupported) { InitializeDynamicTypeInformation(); } @@ -443,7 +452,7 @@ public Type? ElementType { get { - if (!RuntimeFeature.IsDynamicCodeSupported) + if (!IsEnhancedModelMetadataSupported) { throw new NotSupportedException("ElementType is not initialized in ModelMetadata in native AoT."); } @@ -625,6 +634,7 @@ internal void ThrowIfRecordTypeHasValidationOnProperties() } modelType = Nullable.GetUnderlyingType(modelType) ?? modelType; + ParameterBindingMethodCache ??= new ParameterBindingMethodCache(throwOnInvalidMethod: false); return ParameterBindingMethodCache.FindTryParseMethod(modelType); } @@ -744,8 +754,8 @@ private void InitializeTypeInformation() } } - [RequiresUnreferencedCode("Using ModelMetadata with dynamic dependencies enabled is not trim compatible.")] - [RequiresDynamicCode("Using ModelMetadata with dynamic dependencies enabled is not native AOT compatible.")] + [RequiresUnreferencedCode("Using ModelMetadata with IsEnhancedModelMetadataSupport=true is not trim compatible.")] + [RequiresDynamicCode("Using ModelMetadata with IsEnhancedModelMetadataSupport=true is not native AOT compatible.")] private void InitializeDynamicTypeInformation() { Debug.Assert(ModelType != null); diff --git a/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs b/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs index 0d391f36352b..6844fb8cce7a 100644 --- a/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs +++ b/src/Mvc/Mvc.Abstractions/test/ModelBinding/ModelMetadataTest.cs @@ -412,7 +412,7 @@ public void DynamicPropertiesThrowWhenIsDynamicCodeSupportedIsTrue() { var options = new RemoteInvokeOptions(); - options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", false); + options.RuntimeConfigurationOptions.Add("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", false); using var remoteHandle = RemoteExecutor.Invoke(static () => { var metadata = new TestModelMetadata(typeof(DateTime)); @@ -429,7 +429,7 @@ public void DynamicPropertiesSetWhenIsDynamicCodeSupportedIsTrue() { var options = new RemoteInvokeOptions(); - options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", true); + options.RuntimeConfigurationOptions.Add("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", true); using var remoteHandle = RemoteExecutor.Invoke(static () => { var metadata = new TestModelMetadata(typeof(DateTime)); diff --git a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.WarningSuppressions.xml b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.WarningSuppressions.xml new file mode 100644 index 000000000000..79386a19fa8a --- /dev/null +++ b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.WarningSuppressions.xml @@ -0,0 +1,11 @@ + + + + + ILLink + IL2066 + member + M:Microsoft.Extensions.DependencyInjection.EndpointMetadataApiExplorerServiceCollectionExtensions.AddEndpointsApiExplorer(Microsoft.Extensions.DependencyInjection.IServiceCollection) + + + \ No newline at end of file diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml index 182bf4660827..741259748418 100644 --- a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml +++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml @@ -109,6 +109,12 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinder.#cctor + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinder.CreateTryParseOperation(System.Type) + ILLink IL2026 @@ -435,63 +441,21 @@ ILLink - IL2062 - member - M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) - - - ILLink - IL2062 - member - M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) - - - ILLink - IL2062 - member - M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreatePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) - - - ILLink - IL2062 - member - M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) - - - ILLink - IL2062 - member - M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.GetCompatibleCollection``1(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext,System.Nullable{System.Int32}) - - - ILLink - IL2062 - type - T:Microsoft.Extensions.Internal.CopyOnWriteDictionary`2 - - - ILLink - IL2065 - member - M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) - - - ILLink - IL2065 + IL2067 member - M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + M:Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterConfigurationProvider.CreateConfigureDelegate(System.Type) ILLink IL2067 member - M:Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterConfigurationProvider.CreateConfigureDelegate(System.Type) + M:Microsoft.AspNetCore.Mvc.Formatters.InputFormatter.GetDefaultValueForType(System.Type) ILLink IL2067 member - M:Microsoft.AspNetCore.Mvc.Formatters.InputFormatter.GetDefaultValueForType(System.Type) + M:Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.TryGetReader(System.Type,System.Func{System.Object,System.Threading.CancellationToken,System.Threading.Tasks.Task{System.Collections.ICollection}}@) ILLink @@ -601,12 +565,54 @@ member M:Microsoft.AspNetCore.Mvc.Infrastructure.ParameterDefaultValues.GetParameterDefaultValue(System.Reflection.ParameterInfo) + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1.Log.AttemptingToBindCollectionUsingIndices(Microsoft.Extensions.Logging.ILogger,Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DictionaryModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + ILLink IL2072 member M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.BuildFactoryExpression(System.Reflection.ConstructorInfo,System.Linq.Expressions.Expression) + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreatePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + ILLink IL2072 @@ -619,6 +625,12 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertSimpleType(System.Object,System.Type,System.Globalization.CultureInfo) + + ILLink + IL2072 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.GetCompatibleCollection``1(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext,System.Nullable{System.Int32}) + ILLink IL2072 @@ -653,13 +665,13 @@ ILLink IL2075 member - M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinder.#cctor + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) ILLink - IL2103 + IL2075 member - M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.GetPredicateExpression``1(System.Linq.Expressions.Expression{System.Func{``0,System.Object}}) + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj index 4882542748ae..2f0ca390d01f 100644 --- a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj +++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj @@ -16,6 +16,12 @@ Microsoft.AspNetCore.Mvc.RouteAttribute aspnetcore;aspnetcoremvc false enable + + true + false + false diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj index 9d924fd3d640..410c9c785050 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.NativeAotTests/Microsoft.AspNetCore.OpenApi.NativeAotTests.proj @@ -3,7 +3,6 @@ EnableRequestDelegateGenerator - Microsoft.AspNetCore.Http.Generated Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.TrimmingTests/Microsoft.AspNetCore.OpenApi.TrimmingTests.proj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.TrimmingTests/Microsoft.AspNetCore.OpenApi.TrimmingTests.proj new file mode 100644 index 000000000000..881ee4782199 --- /dev/null +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.TrimmingTests/Microsoft.AspNetCore.OpenApi.TrimmingTests.proj @@ -0,0 +1,10 @@ + + + + + EnableRequestDelegateGenerator + Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported + + + + diff --git a/src/Tools/LinkabilityChecker/LinkabilityChecker.csproj b/src/Tools/LinkabilityChecker/LinkabilityChecker.csproj index 3c3a981cac85..6656919a79c8 100644 --- a/src/Tools/LinkabilityChecker/LinkabilityChecker.csproj +++ b/src/Tools/LinkabilityChecker/LinkabilityChecker.csproj @@ -10,11 +10,6 @@ - - From c8e0b39b5eaf59d238c1fa163af940ac10f22a5a Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Fri, 19 Jul 2024 21:43:18 -0700 Subject: [PATCH 14/17] Add RDC/RUC attributes and update exception messages --- ...e.Mvc.Abstractions.WarningSuppressions.xml | 9 +++- .../src/ModelBinding/ModelMetadata.cs | 18 +++++-- ...spNetCore.Mvc.Core.WarningSuppressions.xml | 50 ++++++++++++++++++- 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml index 6d57e59719dc..f25c1b980705 100644 --- a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml +++ b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml @@ -6,7 +6,12 @@ IL2026 member M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.#ctor(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) - This warning is left in the product so developers get an ILLink warning when trimming an app when Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported=true. + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.get_IsComplexType - + \ No newline at end of file diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index f7135f27261f..ffc193753702 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -27,6 +27,9 @@ public abstract class ModelMetadata : IEquatable, IModelMetadata /// public static readonly int DefaultOrder = 10000; + internal const string RequiresUnreferencedCodeMessage = "Resolving this property is not compatible with trimming, as it requires dynamic access to code that is not referenced statically."; + internal const string RequiresDynamicCodeMessage = "Resolving this property may require dynamic code generation."; + /// /// Exposes a feature switch to disable generating model metadata with reflection-heavy strategies. /// This is primarily intended for use in Minimal API-based scenarios where information is derived from @@ -450,11 +453,13 @@ internal IReadOnlyDictionary BoundConstructorPrope /// public Type? ElementType { + [RequiresDynamicCode(RequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] get { if (!IsEnhancedModelMetadataSupported) { - throw new NotSupportedException("ElementType is not initialized in ModelMetadata in native AoT."); + throw new NotSupportedException("ElementType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false."); } return _elementType; } @@ -489,11 +494,13 @@ private set /// public bool IsCollectionType { + [RequiresDynamicCode(RequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] get { if (_isCollectionType == null) { - throw new NotSupportedException("IsCollectionType has not been initialized on this metadata instance."); + throw new NotSupportedException("IsCollectionType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false."); } return _isCollectionType.Value; } @@ -533,11 +540,13 @@ private set /// internal virtual bool IsParseableType { + [RequiresDynamicCode(RequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] get { if (!_isParseableType.HasValue) { - throw new NotSupportedException("IsParseableType has not been initialized on this metadata instance."); + throw new NotSupportedException("IsParseableType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false."); } return _isParseableType.Value; } @@ -555,11 +564,12 @@ private set /// internal bool IsConvertibleType { + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] get { if (!_isConvertibleType.HasValue) { - throw new NotSupportedException("IsConvertibleType has not been initialized on this metadata instance."); + throw new NotSupportedException("IsConvertibleType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false."); } return _isConvertibleType.Value; diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml index 741259748418..b75daebe630a 100644 --- a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml +++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml @@ -97,12 +97,36 @@ member M:Microsoft.AspNetCore.Mvc.JsonOptions.CreateDefaultTypeResolver + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1.<BindComplexCollectionFromIndexes>d__22.MoveNext + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + ILLink IL2026 member M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder.#ctor(System.Type,Microsoft.Extensions.Logging.ILoggerFactory) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + ILLink IL2026 @@ -115,12 +139,24 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinder.CreateTryParseOperation(System.Type) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.TryParseModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + ILLink IL2026 member M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.GetRecordTypeConstructor(System.Type,System.Reflection.ConstructorInfo[]) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata.get_ElementMetadata + ILLink IL2026 @@ -145,6 +181,18 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ConvertSimpleType(System.Object,System.Type,System.Globalization.CultureInfo) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.PropertyValueSetter.SetValue(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,System.Object,System.Object) + + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.DefaultCollectionValidationStrategy.GetEnumeratorForElementType(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,System.Object) + ILLink IL2026 @@ -674,4 +722,4 @@ M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext) - + \ No newline at end of file From c97ea01dff214b8e992d09bb6da98cb8530cbcb9 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 22 Jul 2024 11:46:14 -0700 Subject: [PATCH 15/17] Update suppressions and add more RUC/RDC attributes --- ...e.Mvc.Abstractions.WarningSuppressions.xml | 6 --- .../src/ModelBinding/ModelMetadata.cs | 10 +++- .../src/DefaultApiDescriptionProvider.cs | 3 ++ ...re.Mvc.ApiExplorer.WarningSuppressions.xml | 11 ----- ...spNetCore.Mvc.Core.WarningSuppressions.xml | 48 +++++++++++++++++++ 5 files changed, 60 insertions(+), 18 deletions(-) delete mode 100644 src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.WarningSuppressions.xml diff --git a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml index f25c1b980705..97a42afc46b0 100644 --- a/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml +++ b/src/Mvc/Mvc.Abstractions/src/Microsoft.AspNetCore.Mvc.Abstractions.WarningSuppressions.xml @@ -7,11 +7,5 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.#ctor(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) - - ILLink - IL2026 - member - M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.get_IsComplexType - \ No newline at end of file diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index ffc193753702..907804daebe6 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -477,7 +477,15 @@ private set /// from and without a TryParse method. Most POCO and types are therefore complex. /// Most, if not all, BCL value types are simple types. /// - public bool IsComplexType => !IsConvertibleType && !IsParseableType; + public bool IsComplexType + { + [RequiresDynamicCode(RequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + get + { + return !IsConvertibleType && !IsParseableType; + } + } /// /// Gets a value indicating whether or not is a . diff --git a/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs index 531de27dbef6..0e499dacddd1 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs @@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer; /// by . /// [RequiresUnreferencedCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] +[RequiresDynamicCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] public class DefaultApiDescriptionProvider : IApiDescriptionProvider { private readonly MvcOptions _mvcOptions; @@ -534,6 +535,8 @@ public ApiParameterDescriptionContext( } } + [RequiresUnreferencedCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] + [RequiresDynamicCode("DefaultApiDescriptionProvider is used by MVC which does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")] private sealed class PseudoModelBindingVisitor { public PseudoModelBindingVisitor(ApiParameterContext context, ParameterDescriptor parameter) diff --git a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.WarningSuppressions.xml b/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.WarningSuppressions.xml deleted file mode 100644 index 79386a19fa8a..000000000000 --- a/src/Mvc/Mvc.ApiExplorer/src/Microsoft.AspNetCore.Mvc.ApiExplorer.WarningSuppressions.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - ILLink - IL2066 - member - M:Microsoft.Extensions.DependencyInjection.EndpointMetadataApiExplorerServiceCollectionExtensions.AddEndpointsApiExplorer(Microsoft.Extensions.DependencyInjection.IServiceCollection) - - - \ No newline at end of file diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml index b75daebe630a..450566e81932 100644 --- a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml +++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.WarningSuppressions.xml @@ -43,6 +43,12 @@ member M:Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.OnProvidersExecuting(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelProviderContext) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ApplicationModels.InferParameterBindingInfoConvention.IsComplexTypeParameter(Microsoft.AspNetCore.Mvc.ApplicationModels.ParameterModel,Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata@) + ILLink IL2026 @@ -103,18 +109,36 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1.<BindComplexCollectionFromIndexes>d__22.MoveNext + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinder.<BindPropertyAsync>d__15.MoveNext + ILLink IL2026 member M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexObjectModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.<BindProperty>d__11.MoveNext + ILLink IL2026 member M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinderProvider.GetBinder(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderProviderContext) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Binders.HeaderModelBinderProvider.IsSimpleType(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata) + ILLink IL2026 @@ -151,12 +175,24 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultBindingMetadataProvider.GetRecordTypeConstructor(System.Type,System.Reflection.ConstructorInfo[]) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata.CalculateHasValidators(System.Collections.Generic.HashSet{Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata},Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata) + ILLink IL2026 member M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata.get_ElementMetadata + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata.get_ValidateChildren + ILLink IL2026 @@ -175,6 +211,12 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateSinglePropertyDetails(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ModelMetadataIdentity) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingHelper.ClearValidationStateForModel(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary,System.String) + ILLink IL2026 @@ -193,6 +235,12 @@ member M:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.DefaultCollectionValidationStrategy.GetEnumeratorForElementType(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata,System.Object) + + ILLink + IL2026 + member + M:Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitImplementation(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata@,System.String@,System.Object) + ILLink IL2026 From a29099b921877488e0338f1273a62dfa5f069730 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 22 Jul 2024 12:41:02 -0700 Subject: [PATCH 16/17] Revise ParameterBindingMethodCache initialization and consumption --- .../src/RequestDelegateFactory.cs | 18 ++++++++---------- .../src/ModelBinding/ModelMetadata.cs | 5 +---- src/OpenApi/src/Services/OpenApiGenerator.cs | 12 +++++------- src/Shared/ParameterBindingMethodCache.cs | 6 ++++++ 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index b6a4aeeba7fb..f30dbe63ca10 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -38,8 +38,6 @@ namespace Microsoft.AspNetCore.Http; [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static partial class RequestDelegateFactory { - private static readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); - private static readonly MethodInfo ExecuteTaskWithEmptyResultMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteTaskWithEmptyResult), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo ExecuteValueTaskWithEmptyResultMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteValueTaskWithEmptyResult), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo ExecuteTaskOfTMethod = typeof(RequestDelegateFactory).GetMethod(nameof(ExecuteTaskOfT), BindingFlags.NonPublic | BindingFlags.Static)!; @@ -783,8 +781,8 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat var useSimpleBinding = parameter.ParameterType == typeof(string) || parameter.ParameterType == typeof(StringValues) || parameter.ParameterType == typeof(StringValues?) || - ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType) || - (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)); + ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType) || + (parameter.ParameterType.IsArray && ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType.GetElementType()!)); hasTryParse = useSimpleBinding; return useSimpleBinding ? BindParameterFromFormItem(parameter, formAttribute.Name ?? parameter.Name, factoryContext) @@ -860,12 +858,12 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat { return RequestPipeReaderExpr; } - else if (ParameterBindingMethodCache.HasBindAsyncMethod(parameter)) + else if (ParameterBindingMethodCache.Instance.HasBindAsyncMethod(parameter)) { hasBindAsync = true; return BindParameterFromBindAsync(parameter, factoryContext); } - else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType)) + else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType)) { hasTryParse = true; // 1. We bind from route values only, if route parameters are non-null and the parameter name is in that set. @@ -895,7 +893,7 @@ private static Expression CreateArgument(ParameterInfo parameter, RequestDelegat parameter.ParameterType == typeof(string[]) || parameter.ParameterType == typeof(StringValues) || parameter.ParameterType == typeof(StringValues?) || - (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) + (parameter.ParameterType.IsArray && ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) { // We only infer parameter types if you have an array of TryParsables/string[]/StringValues/StringValues?, and DisableInferredFromBody is true hasTryParse = true; @@ -1565,7 +1563,7 @@ private static Expression BindParameterFromProperties(ParameterInfo parameter, R } var argumentExpression = Expression.Variable(parameter.ParameterType, $"{parameter.Name}_local"); - var (constructor, parameters) = ParameterBindingMethodCache.FindConstructor(parameterType); + var (constructor, parameters) = ParameterBindingMethodCache.Instance.FindConstructor(parameterType); Expression initExpression; @@ -1676,7 +1674,7 @@ private static Expression BindParameterFromValue(ParameterInfo parameter, Expres var isNotNullable = underlyingNullableType is null; var nonNullableParameterType = underlyingNullableType ?? targetParseType; - var tryParseMethodCall = ParameterBindingMethodCache.FindTryParseMethod(nonNullableParameterType); + var tryParseMethodCall = ParameterBindingMethodCache.Instance.FindTryParseMethod(nonNullableParameterType); if (tryParseMethodCall is null) { @@ -1974,7 +1972,7 @@ private static Expression BindParameterFromBindAsync(ParameterInfo parameter, Re var isOptional = IsOptionalParameter(parameter, factoryContext); // Get the BindAsync method for the type. - var bindAsyncMethod = ParameterBindingMethodCache.FindBindAsyncMethod(parameter); + var bindAsyncMethod = ParameterBindingMethodCache.Instance.FindBindAsyncMethod(parameter); // We know BindAsync exists because there's no way to opt-in without defining the method on the type. Debug.Assert(bindAsyncMethod.Expression is not null); diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index 907804daebe6..e6cd088cf93a 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -41,8 +41,6 @@ public abstract class ModelMetadata : IEquatable, IModelMetadata private static bool IsEnhancedModelMetadataSupported { get; } = AppContext.TryGetSwitch("Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported", out var isEnhancedModelMetadataSupported) ? isEnhancedModelMetadataSupported : true; - private static ParameterBindingMethodCache? ParameterBindingMethodCache; - private int? _hashCode; private IReadOnlyList? _boundProperties; private IReadOnlyDictionary? _parameterMapping; @@ -652,8 +650,7 @@ internal void ThrowIfRecordTypeHasValidationOnProperties() } modelType = Nullable.GetUnderlyingType(modelType) ?? modelType; - ParameterBindingMethodCache ??= new ParameterBindingMethodCache(throwOnInvalidMethod: false); - return ParameterBindingMethodCache.FindTryParseMethod(modelType); + return ParameterBindingMethodCache.NonThrowingInstance.FindTryParseMethod(modelType); } [MemberNotNull(nameof(_parameterMapping), nameof(_boundConstructorPropertyMapping))] diff --git a/src/OpenApi/src/Services/OpenApiGenerator.cs b/src/OpenApi/src/Services/OpenApiGenerator.cs index 58a46d3eb835..be70be1edb25 100644 --- a/src/OpenApi/src/Services/OpenApiGenerator.cs +++ b/src/OpenApi/src/Services/OpenApiGenerator.cs @@ -33,8 +33,6 @@ internal sealed class OpenApiGenerator private readonly IHostEnvironment? _environment; private readonly IServiceProviderIsService? _serviceProviderIsService; - internal static readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); - /// /// Creates an instance given an /// and an instance. @@ -259,7 +257,7 @@ private static void GenerateDefaultResponses(Dictionary GetOperationTags(MethodInfo methodInfo, EndpointMetadat private List GetOpenApiParameters(MethodInfo methodInfo, RoutePattern pattern, bool disableInferredBody) { - var parameters = PropertyAsParameterInfo.Flatten(methodInfo.GetParameters(), ParameterBindingMethodCache); + var parameters = PropertyAsParameterInfo.Flatten(methodInfo.GetParameters(), ParameterBindingMethodCache.Instance); var openApiParameters = new List(); foreach (var parameter in parameters) @@ -428,12 +426,12 @@ private List GetOpenApiParameters(MethodInfo methodInfo, Route parameter.ParameterType == typeof(HttpResponse) || parameter.ParameterType == typeof(ClaimsPrincipal) || parameter.ParameterType == typeof(CancellationToken) || - ParameterBindingMethodCache.HasBindAsyncMethod(parameter) || + ParameterBindingMethodCache.Instance.HasBindAsyncMethod(parameter) || _serviceProviderIsService?.IsService(parameter.ParameterType) == true) { return (false, null, null); } - else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType)) + else if (parameter.ParameterType == typeof(string) || ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType)) { // Path vs query cannot be determined by RequestDelegateFactory at startup currently because of the layering, but can be done here. if (parameter.Name is { } name && pattern.GetParameter(name) is not null) @@ -452,7 +450,7 @@ private List GetOpenApiParameters(MethodInfo methodInfo, Route else if (disableInferredBody && ( parameter.ParameterType == typeof(string[]) || parameter.ParameterType == typeof(StringValues) || - (parameter.ParameterType.IsArray && ParameterBindingMethodCache.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) + (parameter.ParameterType.IsArray && ParameterBindingMethodCache.Instance.HasTryParseMethod(parameter.ParameterType.GetElementType()!)))) { return (false, ParameterLocation.Query, null); } diff --git a/src/Shared/ParameterBindingMethodCache.cs b/src/Shared/ParameterBindingMethodCache.cs index d796535c7fab..cac2d2055273 100644 --- a/src/Shared/ParameterBindingMethodCache.cs +++ b/src/Shared/ParameterBindingMethodCache.cs @@ -26,6 +26,12 @@ internal sealed class ParameterBindingMethodCache private static readonly MethodInfo BindAsyncMethod = typeof(ParameterBindingMethodCache).GetMethod(nameof(BindAsync), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo UriTryCreateMethod = typeof(Uri).GetMethod(nameof(Uri.TryCreate), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(UriKind), typeof(Uri).MakeByRefType() })!; + // Thread-safe singletons for ParameterBindingMethodCache + private static readonly Lazy _instance = new(() => new ParameterBindingMethodCache()); + public static ParameterBindingMethodCache Instance = _instance.Value; + private static readonly Lazy _nonThrowingInstance = new(() => new ParameterBindingMethodCache(throwOnInvalidMethod: false)); + public static ParameterBindingMethodCache NonThrowingInstance = _nonThrowingInstance.Value; + // work around https://github.com/dotnet/runtime/issues/81864 by splitting these into a separate class. internal static class SharedExpressions { From e9390a4fbff7a77eb32fe1de0a5cc3ffb5ff0e2a Mon Sep 17 00:00:00 2001 From: wtgodbe Date: Tue, 23 Jul 2024 12:48:30 -0700 Subject: [PATCH 17/17] Update submod --- src/submodules/BlazorMinifiedJs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/submodules/BlazorMinifiedJs b/src/submodules/BlazorMinifiedJs index 42f116d7c126..4cc83c376bb0 160000 --- a/src/submodules/BlazorMinifiedJs +++ b/src/submodules/BlazorMinifiedJs @@ -1 +1 @@ -Subproject commit 42f116d7c126fe087db17b15c8365c27616e1acb +Subproject commit 4cc83c376bb0d472d0c923a45f7dec7af6426d4c