diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/Microsoft.AspNetCore.App.Analyzers.csproj b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/Microsoft.AspNetCore.App.Analyzers.csproj
index 83c09bcf59a2..e68fcbf8ccac 100644
--- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/Microsoft.AspNetCore.App.Analyzers.csproj
+++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/Microsoft.AspNetCore.App.Analyzers.csproj
@@ -27,6 +27,10 @@
+
+
+
+
diff --git a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.Generators.csproj b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.Generators.csproj
index b791dcb5efd8..65cab72b0842 100644
--- a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.Generators.csproj
+++ b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.Generators.csproj
@@ -24,6 +24,10 @@
+
+
+
+
diff --git a/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs b/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs
index 2f7d8116a90f..6d7f46f0699c 100644
--- a/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs
+++ b/src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs
@@ -47,6 +47,7 @@ namespace Microsoft.AspNetCore.Http.Generated
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs
index 837ee73c4a55..8de76e326ce9 100644
--- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs
+++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs
@@ -3,6 +3,7 @@
using System;
using System.Text;
+using Microsoft.CodeAnalysis;
namespace Microsoft.AspNetCore.Http.Generators.StaticRouteHandlerModel.Emitters;
internal static class EndpointParameterEmitter
@@ -35,7 +36,7 @@ internal static string EmitQueryOrHeaderParameterPreparation(this EndpointParame
if (endpointParameter.IsOptional)
{
builder.AppendLine($$"""
- var {{endpointParameter.EmitHandlerArgument()}} = {{endpointParameter.EmitAssigningCodeResult()}}.Count > 0 ? {{endpointParameter.EmitAssigningCodeResult()}}.ToString() : null;
+ var {{endpointParameter.Name}}_temp = {{endpointParameter.EmitAssigningCodeResult()}}.Count > 0 ? {{endpointParameter.Name}}_raw.ToString() : null;
""");
}
else
@@ -45,144 +46,166 @@ internal static string EmitQueryOrHeaderParameterPreparation(this EndpointParame
{
wasParamCheckFailure = true;
}
- var {{endpointParameter.EmitHandlerArgument()}} = {{endpointParameter.EmitAssigningCodeResult()}}.ToString();
+ var {{endpointParameter.Name}}_temp = {{endpointParameter.EmitAssigningCodeResult()}}.ToString();
""");
}
+ builder.Append(endpointParameter.EmitParsingBlock());
+
return builder.ToString();
}
- internal static string EmitRouteParameterPreparation(this EndpointParameter endpointParameter)
+ internal static string EmitParsingBlock(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();
- builder.AppendLine($"""
+
+ if (endpointParameter.IsParsable)
+ {
+ var parsingBlock = endpointParameter.ParsingBlockEmitter($"{endpointParameter.Name}_temp", $"{endpointParameter.Name}_parsed_temp");
+ builder.AppendLine($$"""
+{{parsingBlock}}
+ {{endpointParameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}} {{endpointParameter.EmitHandlerArgument()}} = {{endpointParameter.Name}}_parsed_temp!;
+""");
+
+ }
+ else
+ {
+ builder.AppendLine($$"""
+ {{endpointParameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}} {{endpointParameter.EmitHandlerArgument()}} = {{endpointParameter.Name}}_temp!;
+""");
+
+ }
+
+ return builder.ToString();
+ }
+
+ internal static string EmitRouteParameterPreparation(this EndpointParameter endpointParameter)
+ {
+ var builder = new StringBuilder();
+ builder.AppendLine($"""
{endpointParameter.EmitParameterDiagnosticComment()}
""");
- // Throw an exception of if the route parameter name that was specific in the `FromRoute`
- // attribute or in the parameter name does not appear in the actual route.
- builder.AppendLine($$"""
+ // Throw an exception of if the route parameter name that was specific in the `FromRoute`
+ // attribute or in the parameter name does not appear in the actual route.
+ builder.AppendLine($$"""
if (options?.RouteParameterNames?.Contains("{{endpointParameter.Name}}", StringComparer.OrdinalIgnoreCase) != true)
{
throw new InvalidOperationException($"'{{endpointParameter.Name}}' is not a route parameter.");
}
""");
- var assigningCode = $"httpContext.Request.RouteValues[\"{endpointParameter.Name}\"]?.ToString()";
- builder.AppendLine($$"""
+ var assigningCode = $"httpContext.Request.RouteValues[\"{endpointParameter.Name}\"]?.ToString()";
+ builder.AppendLine($$"""
var {{endpointParameter.EmitAssigningCodeResult()}} = {{assigningCode}};
""");
- if (!endpointParameter.IsOptional)
- {
- builder.AppendLine($$"""
+ if (!endpointParameter.IsOptional)
+ {
+ builder.AppendLine($$"""
if ({{endpointParameter.EmitAssigningCodeResult()}} == null)
{
wasParamCheckFailure = true;
}
""");
- }
- builder.AppendLine($"""
+ }
+ builder.AppendLine($"""
var {endpointParameter.EmitHandlerArgument()} = {endpointParameter.EmitAssigningCodeResult()};
""");
- return builder.ToString();
- }
+ return builder.ToString();
+ }
- internal static string EmitRouteOrQueryParameterPreparation(this EndpointParameter endpointParameter)
- {
- var builder = new StringBuilder();
- builder.AppendLine($"""
+ internal static string EmitRouteOrQueryParameterPreparation(this EndpointParameter endpointParameter)
+ {
+ var builder = new StringBuilder();
+ builder.AppendLine($"""
{endpointParameter.EmitParameterDiagnosticComment()}
""");
- var parameterName = endpointParameter.Name;
- var assigningCode = $@"options?.RouteParameterNames?.Contains(""{parameterName}"", StringComparer.OrdinalIgnoreCase) == true";
- assigningCode += $@"? new StringValues(httpContext.Request.RouteValues[$""{parameterName}""]?.ToString())";
- assigningCode += $@": httpContext.Request.Query[$""{parameterName}""];";
+ var parameterName = endpointParameter.Name;
+ var assigningCode = $@"options?.RouteParameterNames?.Contains(""{parameterName}"", StringComparer.OrdinalIgnoreCase) == true";
+ assigningCode += $@"? new StringValues(httpContext.Request.RouteValues[$""{parameterName}""]?.ToString())";
+ assigningCode += $@": httpContext.Request.Query[$""{parameterName}""];";
- builder.AppendLine($$"""
+ builder.AppendLine($$"""
var {{endpointParameter.EmitAssigningCodeResult()}} = {{assigningCode}};
""");
- if (!endpointParameter.IsOptional)
- {
- builder.AppendLine($$"""
+ if (!endpointParameter.IsOptional)
+ {
+ builder.AppendLine($$"""
if ({{endpointParameter.EmitAssigningCodeResult()}} is StringValues { Count: 0 })
{
wasParamCheckFailure = true;
}
""");
- }
+ }
- builder.AppendLine($"""
+ builder.AppendLine($"""
var {endpointParameter.EmitHandlerArgument()} = {endpointParameter.EmitAssigningCodeResult()};
""");
- return builder.ToString();
- }
+ return builder.ToString();
+ }
- internal static string EmitJsonBodyParameterPreparationString(this EndpointParameter endpointParameter)
- {
- var builder = new StringBuilder();
- builder.AppendLine($"""
+ internal static string EmitJsonBodyParameterPreparationString(this EndpointParameter endpointParameter)
+ {
+ var builder = new StringBuilder();
+ builder.AppendLine($"""
{endpointParameter.EmitParameterDiagnosticComment()}
""");
- var assigningCode = $"await GeneratedRouteBuilderExtensionsCore.TryResolveBody<{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}>(httpContext, {(endpointParameter.IsOptional ? "true" : "false")})";
- builder.AppendLine($$"""
+ var assigningCode = $"await GeneratedRouteBuilderExtensionsCore.TryResolveBody<{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}>(httpContext, {(endpointParameter.IsOptional ? "true" : "false")})";
+ builder.AppendLine($$"""
var (isSuccessful, {{endpointParameter.EmitHandlerArgument()}}) = {{assigningCode}};
""");
- // If binding from the JSON body fails, we exit early. Don't
- // set the status code here because assume it has been set by the
- // TryResolveBody method.
- builder.AppendLine("""
+ // If binding from the JSON body fails, we exit early. Don't
+ // set the status code here because assume it has been set by the
+ // TryResolveBody method.
+ builder.AppendLine("""
if (!isSuccessful)
{
return;
}
""");
- return builder.ToString();
- }
+ return builder.ToString();
+ }
- internal static string EmitServiceParameterPreparation(this EndpointParameter endpointParameter)
- {
- var builder = new StringBuilder();
+ internal static string EmitServiceParameterPreparation(this EndpointParameter endpointParameter)
+ {
+ var builder = new StringBuilder();
- // Preamble for diagnostics purposes.
- builder.AppendLine($"""
+ // Preamble for diagnostics purposes.
+ builder.AppendLine($"""
{endpointParameter.EmitParameterDiagnosticComment()}
""");
- // Requiredness checks for services are handled by the distinction
- // between GetRequiredService and GetService in the assigningCode.
- // Unlike other scenarios, this will result in an exception being thrown
- // at runtime.
- var assigningCode = endpointParameter.IsOptional ?
- $"httpContext.RequestServices.GetService<{endpointParameter.Type}>();" :
- $"httpContext.RequestServices.GetRequiredService<{endpointParameter.Type}>()";
+ // Requiredness checks for services are handled by the distinction
+ // between GetRequiredService and GetService in the assigningCode.
+ // Unlike other scenarios, this will result in an exception being thrown
+ // at runtime.
+ var assigningCode = endpointParameter.IsOptional ?
+ $"httpContext.RequestServices.GetService<{endpointParameter.Type}>();" :
+ $"httpContext.RequestServices.GetRequiredService<{endpointParameter.Type}>()";
- builder.AppendLine($$"""
+ builder.AppendLine($$"""
var {{endpointParameter.EmitHandlerArgument()}} = {{assigningCode}};
""");
- return builder.ToString();
- }
-
- private static string EmitParameterDiagnosticComment(this EndpointParameter endpointParameter) =>
- $"// Endpoint Parameter: {endpointParameter.Name} (Type = {endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}, IsOptional = {endpointParameter.IsOptional}, Source = {endpointParameter.Source})";
+ return builder.ToString();
+ }
- private static string EmitHandlerArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.Name}_local";
- private static string EmitAssigningCodeResult(this EndpointParameter endpointParameter) => $"{endpointParameter.Name}_raw";
+ private static string EmitParameterDiagnosticComment(this EndpointParameter endpointParameter) => $"// Endpoint Parameter: {endpointParameter.Name} (Type = {endpointParameter.Type}, IsOptional = {endpointParameter.IsOptional}, IsParsable = {endpointParameter.IsParsable}, Source = {endpointParameter.Source})";
+ private static string EmitHandlerArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.Name}_local";
+ private static string EmitAssigningCodeResult(this EndpointParameter endpointParameter) => $"{endpointParameter.Name}_raw";
- public static string EmitArgument(this EndpointParameter endpointParameter) => endpointParameter.Source switch
- {
- EndpointParameterSource.JsonBody or EndpointParameterSource.Route or EndpointParameterSource.RouteOrQuery => endpointParameter.IsOptional
- ? endpointParameter.EmitHandlerArgument()
- : $"{endpointParameter.EmitHandlerArgument()}!",
- EndpointParameterSource.Unknown => throw new Exception("Unreachable!"),
- _ => endpointParameter.EmitHandlerArgument()
- };
-}
+ public static string EmitArgument(this EndpointParameter endpointParameter) => endpointParameter.Source switch
+ {
+ EndpointParameterSource.JsonBody or EndpointParameterSource.Route or EndpointParameterSource.RouteOrQuery => endpointParameter.IsOptional ? endpointParameter.EmitHandlerArgument() : $"{endpointParameter.EmitHandlerArgument()}!",
+ EndpointParameterSource.Unknown => throw new Exception("Unreachable!"),
+ _ => endpointParameter.EmitHandlerArgument()
+ };
+ }
diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs
index ef493e6b6d35..fe2056205c0d 100644
--- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs
+++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs
@@ -7,6 +7,9 @@
using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
using Microsoft.CodeAnalysis;
using WellKnownType = Microsoft.AspNetCore.App.Analyzers.Infrastructure.WellKnownTypeData.WellKnownType;
+using Microsoft.AspNetCore.Analyzers.Infrastructure;
+using System.Linq;
+using System.Globalization;
namespace Microsoft.AspNetCore.Http.Generators.StaticRouteHandlerModel;
@@ -34,6 +37,9 @@ public EndpointParameter(IParameterSymbol parameter, WellKnownTypes wellKnownTyp
Source = EndpointParameterSource.Query;
Name = GetParameterName(fromQueryAttribute, parameter.Name);
IsOptional = parameter.IsOptional();
+ AssigningCode = $"httpContext.Request.Query[\"{parameter.Name}\"]";
+ IsParsable = TryGetParsability(parameter, wellKnownTypes, out var parsingBlockEmitter);
+ ParsingBlockEmitter = parsingBlockEmitter;
}
else if (parameter.HasAttributeImplementingInterface(fromHeaderMetadataInterfaceType, out var fromHeaderAttribute))
{
@@ -68,6 +74,93 @@ public EndpointParameter(IParameterSymbol parameter, WellKnownTypes wellKnownTyp
}
}
+ private bool TryGetParsability(IParameterSymbol parameter, WellKnownTypes wellKnownTypes, [NotNullWhen(true)]out Func? parsingBlockEmitter)
+ {
+ var parameterType = parameter.Type.UnwrapTypeSymbol();
+
+ // ParsabilityHelper returns a single enumeration with a Parsable/NonParsable enumeration result. We use this already
+ // in the analyzers to determine whether we need to warn on whether a type needs to implement TryParse/IParsable. To
+ // support usage in the code generator an optiona out parameter has been added to hint at what variant of the various
+ // TryParse methods should be used (this implies that the preferences are baked into ParsabilityHelper). If we aren't
+ // parsable at all we bail.
+ if (ParsabilityHelper.GetParsability(parameterType, wellKnownTypes, out var parsabilityMethod) == Parsability.NotParsable)
+ {
+ parsingBlockEmitter = null;
+ return false;
+ }
+
+ // If we are parsable we need to emit code based on the enumeration ParsabilityMethod which has a bunch of members
+ // which spell out the preferred TryParse uage. This swtich statement makes slight variations to them based on
+ // which method was encountered.
+ Func? preferredTryParseInvocation = parsabilityMethod switch
+ {
+ ParsabilityMethod.IParsable => (string inputArgument, string outputArgument) => $$"""{{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}}.TryParse({{inputArgument}}, CultureInfo.InvariantCulture, out var {{outputArgument}})""",
+ ParsabilityMethod.TryParseWithFormatProvider => (string inputArgument, string outputArgument) => $$"""{{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}}.TryParse({{inputArgument}}, CultureInfo.InvariantCulture, out var {{outputArgument}})""",
+ ParsabilityMethod.TryParse => (string inputArgument, string outputArgument) => $$"""{{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}}.TryParse({{inputArgument}}, out var {{outputArgument}})""",
+ ParsabilityMethod.Enum => (string inputArgument, string outputArgument) => $$"""Enum.TryParse<{{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}}>({{inputArgument}}, out var {{outputArgument}})""",
+ ParsabilityMethod.Uri => (string inputArgument, string outputArgument) => $$"""Uri.TryCreate({{inputArgument}}, UriKind.RelativeOrAbsolute, out var {{outputArgument}})""",
+ ParsabilityMethod.String => null, // string parameters don't require parsing
+ _ => null
+ };
+
+ // Special case handling for specific types
+ if (parameterType.SpecialType == SpecialType.System_Char)
+ {
+ preferredTryParseInvocation = (string inputArgument, string outputArgument) => $$"""{{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}}.TryParse({{inputArgument}}, out var {{outputArgument}})""";
+ }
+ else if (parameterType.SpecialType == SpecialType.System_DateTime)
+ {
+ preferredTryParseInvocation = (string inputArgument, string outputArgument) => $$"""{{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}}.TryParse({{inputArgument}}, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AllowWhiteSpaces, out var {{outputArgument}})""";
+ }
+ else if (SymbolEqualityComparer.Default.Equals(parameterType, wellKnownTypes.Get(WellKnownType.System_DateTimeOffset)))
+ {
+ preferredTryParseInvocation = (string inputArgument, string outputArgument) => $$"""{{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}}.TryParse({{inputArgument}}, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowWhiteSpaces, out var {{outputArgument}})""";
+ }
+ else if (SymbolEqualityComparer.Default.Equals(parameterType, wellKnownTypes.Get(WellKnownType.System_DateOnly)))
+ {
+ preferredTryParseInvocation = (string inputArgument, string outputArgument) => $$"""{{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}}.TryParse({{inputArgument}}, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var {{outputArgument}})""";
+ }
+
+ // ... so for strings (null) we bail.
+ if (preferredTryParseInvocation == null)
+ {
+ parsingBlockEmitter = null;
+ return false;
+ }
+
+ if (IsOptional)
+ {
+ parsingBlockEmitter = (inputArgument, outputArgument) => $$"""
+ {{parameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}} {{outputArgument}} = default;
+ if ({{preferredTryParseInvocation(inputArgument, $"{inputArgument}_parsed_non_nullable")}})
+ {
+ {{outputArgument}} = {{$"{inputArgument}_parsed_non_nullable"}};
+ }
+ else if (string.IsNullOrEmpty({{inputArgument}}))
+ {
+ {{outputArgument}} = null;
+ }
+ else
+ {
+ wasParamCheckFailure = true;
+ }
+""";
+ }
+ else
+ {
+ parsingBlockEmitter = (inputArgument, outputArgument) => $$"""
+ if (!{{preferredTryParseInvocation(inputArgument, outputArgument)}})
+ {
+ wasParamCheckFailure = true;
+ }
+""";
+ }
+
+ // Wrap the TryParse method call in an if-block and if it doesn't work set param check failure.
+ return true;
+
+ }
+
public ITypeSymbol Type { get; }
public EndpointParameterSource Source { get; }
@@ -76,6 +169,9 @@ public EndpointParameter(IParameterSymbol parameter, WellKnownTypes wellKnownTyp
internal string? AssigningCode { get; set; }
public string Name { get; }
public bool IsOptional { get; }
+ [MemberNotNull("ParsingBlockEmitter")]
+ public bool IsParsable { get; }
+ public Func ParsingBlockEmitter { get; }
// TODO: Handle special form types like IFormFileCollection that need special body-reading logic.
private static bool TryGetSpecialTypeAssigningCode(ITypeSymbol type, WellKnownTypes wellKnownTypes, [NotNullWhen(true)] out string? callingCode)
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 7523d383cc99..f4ac18fd7cb1 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
@@ -63,6 +63,7 @@ namespace Microsoft.AspNetCore.Http.Generated
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -1676,4 +1677,4 @@ namespace Microsoft.AspNetCore.Http.Generated
return GetEnumerator();
}
}
-}
\ No newline at end of file
+}
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 03171192a27f..f935d15fd92a 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
@@ -93,6 +93,7 @@ namespace Microsoft.AspNetCore.Http.Generated
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -1755,4 +1756,4 @@ namespace Microsoft.AspNetCore.Http.Generated
return GetEnumerator();
}
}
-}
\ No newline at end of file
+}
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 12fe9c871b46..c4cb5810abc9 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
@@ -63,6 +63,7 @@ namespace Microsoft.AspNetCore.Http.Generated
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -1599,4 +1600,4 @@ namespace Microsoft.AspNetCore.Http.Generated
return GetEnumerator();
}
}
-}
\ No newline at end of file
+}
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 d0e6b6258132..6f5a072ebd52 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
@@ -63,6 +63,7 @@ namespace Microsoft.AspNetCore.Http.Generated
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -119,7 +120,8 @@ namespace Microsoft.AspNetCore.Http.Generated
{
wasParamCheckFailure = true;
}
- var p1_local = p1_raw.ToString();
+ var p1_temp = p1_raw.ToString();
+ var p1_local = p1_temp;
// Endpoint Parameter: p2 (Type = global::System.String, IsOptional = False, Source = Query)
var p2_raw = httpContext.Request.Query["p2"];
@@ -127,7 +129,8 @@ namespace Microsoft.AspNetCore.Http.Generated
{
wasParamCheckFailure = true;
}
- var p2_local = p2_raw.ToString();
+ var p2_temp = p2_raw.ToString();
+ var p2_local = p2_temp;
if (wasParamCheckFailure)
{
@@ -147,7 +150,8 @@ namespace Microsoft.AspNetCore.Http.Generated
{
wasParamCheckFailure = true;
}
- var p1_local = p1_raw.ToString();
+ var p1_temp = p1_raw.ToString();
+ var p1_local = p1_temp;
// Endpoint Parameter: p2 (Type = global::System.String, IsOptional = False, Source = Query)
var p2_raw = httpContext.Request.Query["p2"];
@@ -155,7 +159,8 @@ namespace Microsoft.AspNetCore.Http.Generated
{
wasParamCheckFailure = true;
}
- var p2_local = p2_raw.ToString();
+ var p2_temp = p2_raw.ToString();
+ var p2_local = p2_temp;
if (wasParamCheckFailure)
{
@@ -1627,4 +1632,4 @@ namespace Microsoft.AspNetCore.Http.Generated
return GetEnumerator();
}
}
-}
\ No newline at end of file
+}
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 b9924bfcc456..342aecfbd68c 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
@@ -63,6 +63,7 @@ namespace Microsoft.AspNetCore.Http.Generated
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -1597,4 +1598,4 @@ namespace Microsoft.AspNetCore.Http.Generated
return GetEnumerator();
}
}
-}
\ No newline at end of file
+}
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
new file mode 100644
index 000000000000..961e050a7817
--- /dev/null
+++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt
@@ -0,0 +1,1625 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+#nullable enable
+
+namespace Microsoft.AspNetCore.Builder
+{
+ %GENERATEDCODEATTRIBUTE%
+ internal class SourceKey
+ {
+ public string Path { get; init; }
+ public int Line { get; init; }
+
+ public SourceKey(string path, int line)
+ {
+ Path = path;
+ Line = line;
+ }
+ }
+
+ // This class needs to be internal so that the compiled application
+ // has access to the strongly-typed endpoint definitions that are
+ // generated by the compiler so that they will be favored by
+ // overload resolution and opt the runtime in to the code generated
+ // implementation produced here.
+ %GENERATEDCODEATTRIBUTE%
+ internal static class GenerateRouteBuilderEndpoints
+ {
+ private static readonly string[] GetVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Get };
+ private static readonly string[] PostVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Post };
+ private static readonly string[] PutVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Put };
+ private static readonly string[] DeleteVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Delete };
+ private static readonly string[] PatchVerb = new[] { global::Microsoft.AspNetCore.Http.HttpMethods.Patch };
+
+ internal static global::Microsoft.AspNetCore.Builder.RouteHandlerBuilder MapGet(
+ this global::Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints,
+ [global::System.Diagnostics.CodeAnalysis.StringSyntax("Route")] string pattern,
+ global::System.Func handler,
+ [global::System.Runtime.CompilerServices.CallerFilePath] string filePath = "",
+ [global::System.Runtime.CompilerServices.CallerLineNumber]int lineNumber = 0)
+ {
+ return global::Microsoft.AspNetCore.Http.Generated.GeneratedRouteBuilderExtensionsCore.MapCore(
+ endpoints,
+ pattern,
+ handler,
+ GetVerb,
+ filePath,
+ lineNumber);
+ }
+
+ }
+}
+
+namespace Microsoft.AspNetCore.Http.Generated
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+ using System.Threading.Tasks;
+ using System.IO;
+ using Microsoft.AspNetCore.Routing;
+ using Microsoft.AspNetCore.Routing.Patterns;
+ using Microsoft.AspNetCore.Builder;
+ using Microsoft.AspNetCore.Http;
+ using Microsoft.AspNetCore.Http.Metadata;
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.FileProviders;
+ using Microsoft.Extensions.Primitives;
+
+ using MetadataPopulator = System.Func;
+ using RequestDelegateFactoryFunc = System.Func;
+
+ file static class GeneratedRouteBuilderExtensionsCore
+ {
+
+ private static readonly Dictionary<(string, int), (MetadataPopulator, RequestDelegateFactoryFunc)> map = new()
+ {
+ [(@"TestMapActions.cs", 16)] = (
+ (methodInfo, options) =>
+ {
+ Debug.Assert(options?.EndpointBuilder != null, "EndpointBuilder not found.");
+ options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 16));
+ return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() };
+ },
+ (del, options, inferredMetadataResult) =>
+ {
+ var handler = (Func)del;
+ EndpointFilterDelegate? filteredInvocation = null;
+
+ if (options?.EndpointBuilder?.FilterFactories.Count > 0)
+ {
+ filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic =>
+ {
+ if (ic.HttpContext.Response.StatusCode == 400)
+ {
+ return ValueTask.FromResult