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(Results.Empty); + } + return ValueTask.FromResult(handler(ic.GetArgument(0))); + }, + options.EndpointBuilder, + handler.Method); + } + + Task RequestHandler(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = TestMapActions.Todo, IsOptional = False, Source = Query) + var p_raw = httpContext.Request.Query["p"]; + if (StringValues.IsNullOrEmpty(p_raw)) + { + wasParamCheckFailure = true; + } + var p_temp = p_raw.ToString(); + if (!global::TestMapActions.Todo.TryParse(p_temp, out var p_parsed_temp)) + { + wasParamCheckFailure = true; + } + var p_local = p_parsed_temp!; + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + return Task.CompletedTask; + } + httpContext.Response.ContentType ??= "text/plain"; + var result = handler(p_local); + return httpContext.Response.WriteAsync(result); + } + async Task RequestHandlerFiltered(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = TestMapActions.Todo, IsOptional = False, Source = Query) + var p_raw = httpContext.Request.Query["p"]; + if (StringValues.IsNullOrEmpty(p_raw)) + { + wasParamCheckFailure = true; + } + var p_temp = p_raw.ToString(); + if (!global::TestMapActions.Todo.TryParse(p_temp, out var p_parsed_temp)) + { + wasParamCheckFailure = true; + } + var p_local = p_parsed_temp!; + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + } + var result = await filteredInvocation(new EndpointFilterInvocationContext(httpContext, p_local)); + await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); + } + + RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered; + var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection.Empty; + return new RequestDelegateResult(targetDelegate, metadata); + }), + + }; + + internal static RouteHandlerBuilder MapCore( + this IEndpointRouteBuilder routes, + string pattern, + Delegate handler, + IEnumerable httpMethods, + string filePath, + int lineNumber) + { + var (populateMetadata, createRequestDelegate) = map[(filePath, lineNumber)]; + return RouteHandlerServices.Map(routes, pattern, handler, httpMethods, populateMetadata, createRequestDelegate); + } + + private static EndpointFilterDelegate BuildFilterDelegate(EndpointFilterDelegate filteredInvocation, EndpointBuilder builder, MethodInfo mi) + { + var routeHandlerFilters = builder.FilterFactories; + var context0 = new EndpointFilterFactoryContext + { + MethodInfo = mi, + ApplicationServices = builder.ApplicationServices, + }; + var initialFilteredInvocation = filteredInvocation; + for (var i = routeHandlerFilters.Count - 1; i >= 0; i--) + { + var filterFactory = routeHandlerFilters[i]; + filteredInvocation = filterFactory(context0, filteredInvocation); + } + return filteredInvocation; + } + + private static Task ExecuteObjectResult(object? obj, HttpContext httpContext) + { + if (obj is IResult r) + { + return r.ExecuteAsync(httpContext); + } + else if (obj is string s) + { + return httpContext.Response.WriteAsync(s); + } + else + { + return httpContext.Response.WriteAsJsonAsync(obj); + } + } + + private static async ValueTask<(bool, T?)> TryResolveBody(HttpContext httpContext, bool allowEmpty) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (IOException) + { + return (false, default); + } + catch (System.Text.Json.JsonException) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + return (false, default); + } + } + + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0) + { + HttpContext = httpContext; + Arg0 = arg0; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + + public int Count => 1; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + + public int Count => 2; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + + public int Count => 3; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + + public int Count => 4; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + + public int Count => 5; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + + public int Count => 6; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + + public int Count => 7; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + + public int Count => 8; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + Arg8 = arg8; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + 8 => Arg8, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + case 8: + Arg8 = (T8)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + public T8 Arg8 { get; set; } + + public int Count => 9; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + 8 => (T)(object)Arg8!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + Arg8 = arg8; + Arg9 = arg9; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + 8 => Arg8, + 9 => Arg9, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + case 8: + Arg8 = (T8)(object?)value!; + break; + case 9: + Arg9 = (T9)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + public T8 Arg8 { get; set; } + public T9 Arg9 { get; set; } + + public int Count => 10; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + 8 => (T)(object)Arg8!, + 9 => (T)(object)Arg9!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} 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 new file mode 100644 index 000000000000..1a7e644a147d --- /dev/null +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_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(Results.Empty); + } + return ValueTask.FromResult(handler(ic.GetArgument(0))); + }, + options.EndpointBuilder, + handler.Method); + } + + Task RequestHandler(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = TestMapActions.TodoStatus, IsOptional = False, Source = Query) + var p_raw = httpContext.Request.Query["p"]; + if (StringValues.IsNullOrEmpty(p_raw)) + { + wasParamCheckFailure = true; + } + var p_temp = p_raw.ToString(); + if (!Enum.TryParse(p_temp, out var p_parsed_temp)) + { + wasParamCheckFailure = true; + } + var p_local = p_parsed_temp!; + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + return Task.CompletedTask; + } + httpContext.Response.ContentType ??= "text/plain"; + var result = handler(p_local); + return httpContext.Response.WriteAsync(result); + } + async Task RequestHandlerFiltered(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = TestMapActions.TodoStatus, IsOptional = False, Source = Query) + var p_raw = httpContext.Request.Query["p"]; + if (StringValues.IsNullOrEmpty(p_raw)) + { + wasParamCheckFailure = true; + } + var p_temp = p_raw.ToString(); + if (!Enum.TryParse(p_temp, out var p_parsed_temp)) + { + wasParamCheckFailure = true; + } + var p_local = p_parsed_temp!; + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + } + var result = await filteredInvocation(new EndpointFilterInvocationContext(httpContext, p_local)); + await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); + } + + RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered; + var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection.Empty; + return new RequestDelegateResult(targetDelegate, metadata); + }), + + }; + + internal static RouteHandlerBuilder MapCore( + this IEndpointRouteBuilder routes, + string pattern, + Delegate handler, + IEnumerable httpMethods, + string filePath, + int lineNumber) + { + var (populateMetadata, createRequestDelegate) = map[(filePath, lineNumber)]; + return RouteHandlerServices.Map(routes, pattern, handler, httpMethods, populateMetadata, createRequestDelegate); + } + + private static EndpointFilterDelegate BuildFilterDelegate(EndpointFilterDelegate filteredInvocation, EndpointBuilder builder, MethodInfo mi) + { + var routeHandlerFilters = builder.FilterFactories; + var context0 = new EndpointFilterFactoryContext + { + MethodInfo = mi, + ApplicationServices = builder.ApplicationServices, + }; + var initialFilteredInvocation = filteredInvocation; + for (var i = routeHandlerFilters.Count - 1; i >= 0; i--) + { + var filterFactory = routeHandlerFilters[i]; + filteredInvocation = filterFactory(context0, filteredInvocation); + } + return filteredInvocation; + } + + private static Task ExecuteObjectResult(object? obj, HttpContext httpContext) + { + if (obj is IResult r) + { + return r.ExecuteAsync(httpContext); + } + else if (obj is string s) + { + return httpContext.Response.WriteAsync(s); + } + else + { + return httpContext.Response.WriteAsJsonAsync(obj); + } + } + + private static async ValueTask<(bool, T?)> TryResolveBody(HttpContext httpContext, bool allowEmpty) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (IOException) + { + return (false, default); + } + catch (System.Text.Json.JsonException) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + return (false, default); + } + } + + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0) + { + HttpContext = httpContext; + Arg0 = arg0; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + + public int Count => 1; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + + public int Count => 2; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + + public int Count => 3; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + + public int Count => 4; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + + public int Count => 5; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + + public int Count => 6; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + + public int Count => 7; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + + public int Count => 8; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + Arg8 = arg8; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + 8 => Arg8, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + case 8: + Arg8 = (T8)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + public T8 Arg8 { get; set; } + + public int Count => 9; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + 8 => (T)(object)Arg8!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + Arg8 = arg8; + Arg9 = arg9; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + 8 => Arg8, + 9 => Arg9, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + case 8: + Arg8 = (T8)(object?)value!; + break; + case 9: + Arg9 = (T9)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + public T8 Arg8 { get; set; } + public T9 Arg9 { get; set; } + + public int Count => 10; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + 8 => (T)(object)Arg8!, + 9 => (T)(object)Arg9!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} 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 629831833520..b755b15ed365 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 @@ -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; @@ -115,7 +116,8 @@ namespace Microsoft.AspNetCore.Http.Generated var wasParamCheckFailure = false; // Endpoint Parameter: p (Type = global::System.String?, IsOptional = True, Source = Query) var p_raw = httpContext.Request.Query["p"]; - var p_local = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_temp = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_local = p_temp; if (wasParamCheckFailure) { @@ -131,7 +133,8 @@ namespace Microsoft.AspNetCore.Http.Generated var wasParamCheckFailure = false; // Endpoint Parameter: p (Type = global::System.String?, IsOptional = True, Source = Query) var p_raw = httpContext.Request.Query["p"]; - var p_local = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_temp = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_local = p_temp; if (wasParamCheckFailure) { @@ -1603,4 +1606,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_SingleNullableStringParam_WithQueryStringValueProvided_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithQueryStringValueProvided_StringReturn.generated.txt index 0a88867dc913..6aff493747fc 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithQueryStringValueProvided_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithQueryStringValueProvided_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; @@ -115,7 +116,8 @@ namespace Microsoft.AspNetCore.Http.Generated var wasParamCheckFailure = false; // Endpoint Parameter: p (Type = string?, IsOptional = True, Source = Query) var p_raw = httpContext.Request.Query["p"]; - var p_local = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_temp = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_local = p_temp; if (wasParamCheckFailure) { @@ -131,7 +133,8 @@ namespace Microsoft.AspNetCore.Http.Generated var wasParamCheckFailure = false; // Endpoint Parameter: p (Type = string?, IsOptional = True, Source = Query) var p_raw = httpContext.Request.Query["p"]; - var p_local = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_temp = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_local = p_temp; if (wasParamCheckFailure) { @@ -1610,4 +1613,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_SingleNullableStringParam_WithoutQueryStringValueProvided_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithoutQueryStringValueProvided_StringReturn.generated.txt index 0a88867dc913..6aff493747fc 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithoutQueryStringValueProvided_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithoutQueryStringValueProvided_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; @@ -115,7 +116,8 @@ namespace Microsoft.AspNetCore.Http.Generated var wasParamCheckFailure = false; // Endpoint Parameter: p (Type = string?, IsOptional = True, Source = Query) var p_raw = httpContext.Request.Query["p"]; - var p_local = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_temp = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_local = p_temp; if (wasParamCheckFailure) { @@ -131,7 +133,8 @@ namespace Microsoft.AspNetCore.Http.Generated var wasParamCheckFailure = false; // Endpoint Parameter: p (Type = string?, IsOptional = True, Source = Query) var p_raw = httpContext.Request.Query["p"]; - var p_local = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_temp = p_raw.Count > 0 ? p_raw.ToString() : null; + var p_local = p_temp; if (wasParamCheckFailure) { @@ -1610,4 +1613,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_SingleStringParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleStringParam_StringReturn.generated.txt index 87e2f4a1c6ef..a67a23337552 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleStringParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleStringParam_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 p_local = p_raw.ToString(); + var p_temp = p_raw.ToString(); + var p_local = p_temp; if (wasParamCheckFailure) { @@ -139,7 +141,8 @@ namespace Microsoft.AspNetCore.Http.Generated { wasParamCheckFailure = true; } - var p_local = p_raw.ToString(); + var p_temp = p_raw.ToString(); + var p_local = p_temp; if (wasParamCheckFailure) { @@ -1618,4 +1621,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_SingleTimeOnlyParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleTimeOnlyParam_StringReturn.generated.txt new file mode 100644 index 000000000000..b1b71b101d22 --- /dev/null +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleTimeOnlyParam_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(Results.Empty); + } + return ValueTask.FromResult(handler(ic.GetArgument(0))); + }, + options.EndpointBuilder, + handler.Method); + } + + Task RequestHandler(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = System.TimeOnly, IsOptional = False, Source = Query) + var p_raw = httpContext.Request.Query["p"]; + if (StringValues.IsNullOrEmpty(p_raw)) + { + wasParamCheckFailure = true; + } + var p_temp = p_raw.ToString(); + if (!global::System.TimeOnly.TryParse(p_temp, CultureInfo.InvariantCulture, out var p_parsed_temp)) + { + wasParamCheckFailure = true; + } + var p_local = p_parsed_temp!; + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + return Task.CompletedTask; + } + httpContext.Response.ContentType ??= "text/plain"; + var result = handler(p_local); + return httpContext.Response.WriteAsync(result); + } + async Task RequestHandlerFiltered(HttpContext httpContext) + { + var wasParamCheckFailure = false; + // Endpoint Parameter: p (Type = System.TimeOnly, IsOptional = False, Source = Query) + var p_raw = httpContext.Request.Query["p"]; + if (StringValues.IsNullOrEmpty(p_raw)) + { + wasParamCheckFailure = true; + } + var p_temp = p_raw.ToString(); + if (!global::System.TimeOnly.TryParse(p_temp, CultureInfo.InvariantCulture, out var p_parsed_temp)) + { + wasParamCheckFailure = true; + } + var p_local = p_parsed_temp!; + + if (wasParamCheckFailure) + { + httpContext.Response.StatusCode = 400; + } + var result = await filteredInvocation(new EndpointFilterInvocationContext(httpContext, p_local)); + await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext); + } + + RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered; + var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection.Empty; + return new RequestDelegateResult(targetDelegate, metadata); + }), + + }; + + internal static RouteHandlerBuilder MapCore( + this IEndpointRouteBuilder routes, + string pattern, + Delegate handler, + IEnumerable httpMethods, + string filePath, + int lineNumber) + { + var (populateMetadata, createRequestDelegate) = map[(filePath, lineNumber)]; + return RouteHandlerServices.Map(routes, pattern, handler, httpMethods, populateMetadata, createRequestDelegate); + } + + private static EndpointFilterDelegate BuildFilterDelegate(EndpointFilterDelegate filteredInvocation, EndpointBuilder builder, MethodInfo mi) + { + var routeHandlerFilters = builder.FilterFactories; + var context0 = new EndpointFilterFactoryContext + { + MethodInfo = mi, + ApplicationServices = builder.ApplicationServices, + }; + var initialFilteredInvocation = filteredInvocation; + for (var i = routeHandlerFilters.Count - 1; i >= 0; i--) + { + var filterFactory = routeHandlerFilters[i]; + filteredInvocation = filterFactory(context0, filteredInvocation); + } + return filteredInvocation; + } + + private static Task ExecuteObjectResult(object? obj, HttpContext httpContext) + { + if (obj is IResult r) + { + return r.ExecuteAsync(httpContext); + } + else if (obj is string s) + { + return httpContext.Response.WriteAsync(s); + } + else + { + return httpContext.Response.WriteAsJsonAsync(obj); + } + } + + private static async ValueTask<(bool, T?)> TryResolveBody(HttpContext httpContext, bool allowEmpty) + { + var feature = httpContext.Features.Get(); + + if (feature?.CanHaveBody == true) + { + if (!httpContext.Request.HasJsonContentType()) + { + httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; + return (false, default); + } + try + { + var bodyValue = await httpContext.Request.ReadFromJsonAsync(); + if (!allowEmpty && bodyValue == null) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, bodyValue); + } + return (true, bodyValue); + } + catch (IOException) + { + return (false, default); + } + catch (System.Text.Json.JsonException) + { + httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + return (false, default); + } + } + return (false, default); + } + } + + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0) + { + HttpContext = httpContext; + Arg0 = arg0; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + + public int Count => 1; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + + public int Count => 2; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + + public int Count => 3; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + + public int Count => 4; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + + public int Count => 5; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + + public int Count => 6; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + + public int Count => 7; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + + public int Count => 8; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + Arg8 = arg8; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + 8 => Arg8, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + case 8: + Arg8 = (T8)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + public T8 Arg8 { get; set; } + + public int Count => 9; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + 8 => (T)(object)Arg8!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + %GENERATEDCODEATTRIBUTE% + file class EndpointFilterInvocationContext : EndpointFilterInvocationContext, IList + { + internal EndpointFilterInvocationContext(HttpContext httpContext, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + { + HttpContext = httpContext; + Arg0 = arg0; + Arg1 = arg1; + Arg2 = arg2; + Arg3 = arg3; + Arg4 = arg4; + Arg5 = arg5; + Arg6 = arg6; + Arg7 = arg7; + Arg8 = arg8; + Arg9 = arg9; + } + + public object? this[int index] + { + get => index switch + { + 0 => Arg0, + 1 => Arg1, + 2 => Arg2, + 3 => Arg3, + 4 => Arg4, + 5 => Arg5, + 6 => Arg6, + 7 => Arg7, + 8 => Arg8, + 9 => Arg9, + _ => new IndexOutOfRangeException() + }; + set + { + switch (index) + { + case 0: + Arg0 = (T0)(object?)value!; + break; + case 1: + Arg1 = (T1)(object?)value!; + break; + case 2: + Arg2 = (T2)(object?)value!; + break; + case 3: + Arg3 = (T3)(object?)value!; + break; + case 4: + Arg4 = (T4)(object?)value!; + break; + case 5: + Arg5 = (T5)(object?)value!; + break; + case 6: + Arg6 = (T6)(object?)value!; + break; + case 7: + Arg7 = (T7)(object?)value!; + break; + case 8: + Arg8 = (T8)(object?)value!; + break; + case 9: + Arg9 = (T9)(object?)value!; + break; + default: + break; + } + } + } + + public override HttpContext HttpContext { get; } + + public override IList Arguments => this; + + public T0 Arg0 { get; set; } + public T1 Arg1 { get; set; } + public T2 Arg2 { get; set; } + public T3 Arg3 { get; set; } + public T4 Arg4 { get; set; } + public T5 Arg5 { get; set; } + public T6 Arg6 { get; set; } + public T7 Arg7 { get; set; } + public T8 Arg8 { get; set; } + public T9 Arg9 { get; set; } + + public int Count => 10; + + public bool IsReadOnly => false; + + public bool IsFixedSize => true; + + public void Add(object? item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(object? item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(object?[] array, int arrayIndex) + { + for (int i = 0; i < Arguments.Count; i++) + { + array[arrayIndex++] = Arguments[i]; + } + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Arguments.Count; i++) + { + yield return Arguments[i]; + } + } + + public override T GetArgument(int index) + { + return index switch + { + 0 => (T)(object)Arg0!, + 1 => (T)(object)Arg1!, + 2 => (T)(object)Arg2!, + 3 => (T)(object)Arg3!, + 4 => (T)(object)Arg4!, + 5 => (T)(object)Arg5!, + 6 => (T)(object)Arg6!, + 7 => (T)(object)Arg7!, + 8 => (T)(object)Arg8!, + 9 => (T)(object)Arg9!, + _ => throw new IndexOutOfRangeException() + }; + } + + public int IndexOf(object? item) + { + return Arguments.IndexOf(item); + } + + public void Insert(int index, object? item) + { + throw new NotSupportedException(); + } + + public bool Remove(object? item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} 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 710b0e849edc..4a41666f9501 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 @@ -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; @@ -1792,4 +1793,4 @@ namespace Microsoft.AspNetCore.Http.Generated return GetEnumerator(); } } -} \ No newline at end of file +} 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 e1c2ea654f0f..3d2033c6d838 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 @@ -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; @@ -1739,4 +1740,4 @@ namespace Microsoft.AspNetCore.Http.Generated return GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTestBase.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTestBase.cs index b9a57c00683c..b0d1e00a9b3d 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTestBase.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTestBase.cs @@ -190,6 +190,11 @@ private static string GetMapActionString(string sources) => $$""" using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; +using System.Reflection; +using System.Reflection.Metadata; +using System.Net; +using System.Net.Sockets; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -207,6 +212,109 @@ public static IEndpointRouteBuilder MapTestEndpoints(this IEndpointRouteBuilder return app; } } + +public enum TodoStatus +{ + Trap, // A trap for Enum.TryParse! + Done, + InProgress, + NotDone +} + +public interface ITodo +{ + public int Id { get; } + public string? Name { get; } + public bool IsComplete { get; } + public TodoStatus Status { get; } +} + +public class PrecedenceCheckTodo +{ + public PrecedenceCheckTodo(int magicValue) + { + MagicValue = magicValue; + } + public int MagicValue { get; } + public static bool TryParse(string? input, IFormatProvider? provider, out PrecedenceCheckTodo result) + { + result = new PrecedenceCheckTodo(42); + return true; + } + public static bool TryParse(string? input, out PrecedenceCheckTodo result) + { + result = new PrecedenceCheckTodo(24); + return true; + } +} + +public class PrecedenceCheckTodoWithoutFormat +{ + public PrecedenceCheckTodoWithoutFormat(int magicValue) + { + MagicValue = magicValue; + } + public int MagicValue { get; } + public static bool TryParse(string? input, out PrecedenceCheckTodoWithoutFormat result) + { + result = new PrecedenceCheckTodoWithoutFormat(24); + return true; + } +} + +public class ParsableTodo : IParsable +{ + public int Id { get; set; } + public string? Name { get; set; } = "Todo"; + public bool IsComplete { get; set; } + public static ParsableTodo Parse(string s, IFormatProvider? provider) + { + return new ParsableTodo(); + } + public static bool TryParse(string? input, IFormatProvider? provider, out ParsableTodo result) + { + if (input == "1") + { + result = new ParsableTodo + { + Id = 1, + Name = "Knit kitten mittens.", + IsComplete = false + }; + return true; + } + else + { + result = null!; + return false; + } + } +} + +public class Todo +{ + public int Id { get; set; } + public string? Name { get; set; } = "Todo"; + public bool IsComplete { get; set; } + public static bool TryParse(string input, out Todo? result) + { + if (input == "1") + { + result = new Todo + { + Id = 1, + Name = "Knit kitten mittens.", + IsComplete = false + }; + return true; + } + else + { + result = null; + return false; + } + } +} """; private static Task CreateCompilationAsync(string sources) { @@ -248,6 +356,7 @@ internal async Task VerifyAgainstBaselineUsingFile(Compilation compilation, [Cal var baselineFilePath = Path.Combine("RequestDelegateGenerator", "Baselines", $"{callerName}.generated.txt"); var generatedSyntaxTree = compilation.SyntaxTrees.Last(); var generatedCode = await generatedSyntaxTree.GetTextAsync(); + await File.WriteAllTextAsync(baselineFilePath, generatedCode.ToString()); var baseline = await File.ReadAllTextAsync(baselineFilePath); var expectedLines = baseline .TrimEnd() // Trim newlines added by autoformat diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTests.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTests.cs index 8766a3f5e58e..7747f5ab050f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTests.cs @@ -2,13 +2,22 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System; using System.Globalization; +using System.Net.Sockets; +using System.Net; +using System.Numerics; +using System.Reflection.Metadata; +using System.Reflection; using System.Text.Json; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Generators.StaticRouteHandlerModel; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.DependencyInjection; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.WebEncoders.Testing; namespace Microsoft.AspNetCore.Http.Generators.Tests; @@ -95,6 +104,206 @@ public async Task MapAction_ExplicitQueryParam_StringReturn(string source, strin await VerifyResponseBodyAsync(httpContext, expectedBody, expectedStatusCode); } + [Fact] + public async Task MapAction_SingleTimeOnlyParam_StringReturn() + { + var (results, compilation) = await RunGeneratorAsync(""" +app.MapGet("/hello", ([FromQuery]TimeOnly p) => p.ToString()); +"""); + var endpointModel = GetStaticEndpoint(results, GeneratorSteps.EndpointModelStep); + var endpoint = GetEndpointFromCompilation(compilation); + + Assert.Equal("/hello", endpointModel.RoutePattern); + Assert.Equal("MapGet", endpointModel.HttpMethod); + var p = Assert.Single(endpointModel.Parameters); + Assert.Equal(EndpointParameterSource.Query, p.Source); + Assert.Equal("p", p.Name); + + var httpContext = CreateHttpContext(); + httpContext.Request.QueryString = new QueryString("?p=13:30"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "1:30 PM"); + await VerifyAgainstBaselineUsingFile(compilation); + } + + [Theory] + [InlineData("DateOnly", "2023-02-20")] + [InlineData("DateTime", "2023-02-20")] + [InlineData("DateTimeOffset", "2023-02-20")] + public async Task MapAction_SingleDateLikeParam_StringReturn(string parameterType, string result) + { + var (results, compilation) = await RunGeneratorAsync($$""" +app.MapGet("/hello", ([FromQuery]{{parameterType}} p) => p.ToString("yyyy-MM-dd")); +"""); + var endpointModel = GetStaticEndpoint(results, GeneratorSteps.EndpointModelStep); + var endpoint = GetEndpointFromCompilation(compilation); + + Assert.Equal("/hello", endpointModel.RoutePattern); + Assert.Equal("MapGet", endpointModel.HttpMethod); + var p = Assert.Single(endpointModel.Parameters); + Assert.Equal(EndpointParameterSource.Query, p.Source); + Assert.Equal("p", p.Name); + + var httpContext = CreateHttpContext(); + httpContext.Request.QueryString = new QueryString($"?p={result}"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, result); + } + + public static object[][] TryParsableParameters + { + get + { + var now = DateTime.Now; + + return new[] + { + //// string is not technically "TryParsable", but it's the special case. + new object[] { "string", "plain string", "plain string" }, + new object[] { "int", "-42", -42 }, + new object[] { "uint", "42", 42U }, + new object[] { "bool", "true", true }, + new object[] { "short", "-42", (short)-42 }, + new object[] { "ushort", "42", (ushort)42 }, + new object[] { "long", "-42", -42L }, + new object[] { "ulong", "42", 42UL }, + new object[] { "IntPtr", "-42", new IntPtr(-42) }, + new object[] { "char", "A", 'A' }, + new object[] { "double", "0.5", 0.5 }, + new object[] { "float", "0.5", 0.5f }, + new object[] { "Half", "0.5", (Half)0.5f }, + new object[] { "decimal", "0.5", 0.5m }, + new object[] { "Uri", "https://example.org", new Uri("https://example.org") }, + new object[] { "DateTime", now.ToString("o"), now.ToUniversalTime() }, + new object[] { "DateTimeOffset", "1970-01-01T00:00:00.0000000+00:00", DateTimeOffset.UnixEpoch }, + new object[] { "TimeSpan", "00:00:42", TimeSpan.FromSeconds(42) }, + new object[] { "Guid", "00000000-0000-0000-0000-000000000000", Guid.Empty }, + new object[] { "Version", "6.0.0.42", new Version("6.0.0.42") }, + new object[] { "BigInteger", "-42", new BigInteger(-42) }, + new object[] { "IPAddress", "127.0.0.1", IPAddress.Loopback }, + new object[] { "IPEndPoint", "127.0.0.1:80", new IPEndPoint(IPAddress.Loopback, 80) }, + new object[] { "AddressFamily", "Unix", AddressFamily.Unix }, + new object[] { "ILOpCode", "Nop", ILOpCode.Nop }, + new object[] { "AssemblyFlags", "PublicKey,Retargetable", AssemblyFlags.PublicKey | AssemblyFlags.Retargetable }, + new object[] { "int?", "42", 42 }, + new object[] { "int?", null, null }, + }; + } + } + + [Theory] + [MemberData(nameof(TryParsableParameters))] + public async Task MapAction_SingleParsable_StringReturn(string typeName, string queryStringInput, object expectedParameterValue) + { + var (results, compilation) = await RunGeneratorAsync($$""" +app.MapGet("/hello", (HttpContext context, [FromQuery]{{typeName}} p) => +{ + context.Items["tryParsable"] = p; +}); +"""); + + var endpointModel = GetStaticEndpoint(results, GeneratorSteps.EndpointModelStep); + var endpoint = GetEndpointFromCompilation(compilation); + var httpContext = CreateHttpContext(); + + var encodedQueryStringInput = queryStringInput != null ? UrlEncoder.Default.Encode(queryStringInput) : null; + httpContext.Request.QueryString = new QueryString($"?p={encodedQueryStringInput}"); + + await endpoint.RequestDelegate(httpContext); + Assert.Equal(200, httpContext.Response.StatusCode); + Assert.Equal(expectedParameterValue, httpContext.Items["tryParsable"]); + } + + [Theory] + [InlineData("PrecedenceCheckTodoWithoutFormat", "24")] + [InlineData("PrecedenceCheckTodo", "42")] + public async Task MapAction_TryParsePrecedenceCheck(string parameterType, string result) + { + var (results, compilation) = await RunGeneratorAsync($$""" +app.MapGet("/hello", ([FromQuery]{{parameterType}} p) => p.MagicValue); +"""); + + var endpointModel = GetStaticEndpoint(results, GeneratorSteps.EndpointModelStep); + var endpoint = GetEndpointFromCompilation(compilation); + + Assert.Equal("/hello", endpointModel.RoutePattern); + Assert.Equal("MapGet", endpointModel.HttpMethod); + var p = Assert.Single(endpointModel.Parameters); + Assert.Equal(EndpointParameterSource.Query, p.Source); + Assert.Equal("p", p.Name); + + var httpContext = CreateHttpContext(); + httpContext.Request.QueryString = new QueryString("?p=1"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, result); + } + + [Fact] + public async Task MapAction_SingleComplexTypeParam_StringReturn() + { + // HACK! Notice the return value of p.Name! - this is because TestMapActions.cs has #nullable enable + // set and the compiler is returning when it is simply p.Name: + // + // CS8603: Possible null reference return. + // + // Without source gen this same code isn't a problem. + var (results, compilation) = await RunGeneratorAsync(""" +app.MapGet("/hello", ([FromQuery]Todo p) => p.Name!); +"""); + + var endpointModel = GetStaticEndpoint(results, GeneratorSteps.EndpointModelStep); + var endpoint = GetEndpointFromCompilation(compilation); + + Assert.Equal("/hello", endpointModel.RoutePattern); + Assert.Equal("MapGet", endpointModel.HttpMethod); + var p = Assert.Single(endpointModel.Parameters); + Assert.Equal(EndpointParameterSource.Query, p.Source); + Assert.Equal("p", p.Name); + + var httpContext = CreateHttpContext(); + httpContext.Request.QueryString = new QueryString("?p=1"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "Knit kitten mittens."); + await VerifyAgainstBaselineUsingFile(compilation); + } + + [Fact] + public async Task MapAction_SingleEnumParam_StringReturn() + { + // HACK! Notice the return value of p.Name! - this is because TestMapActions.cs has #nullable enable + // set and the compiler is returning when it is simply p.Name: + // + // CS8603: Possible null reference return. + // + // Without source gen this same code isn't a problem. + var (results, compilation) = await RunGeneratorAsync(""" +app.MapGet("/hello", ([FromQuery]TodoStatus p) => p.ToString()); +"""); + + var endpointModel = GetStaticEndpoint(results, GeneratorSteps.EndpointModelStep); + var endpoint = GetEndpointFromCompilation(compilation); + + Assert.Equal("/hello", endpointModel.RoutePattern); + Assert.Equal("MapGet", endpointModel.HttpMethod); + var p = Assert.Single(endpointModel.Parameters); + Assert.Equal(EndpointParameterSource.Query, p.Source); + Assert.Equal("p", p.Name); + + var httpContext = CreateHttpContext(); + httpContext.Request.QueryString = new QueryString("?p=Done"); + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "Done"); + await VerifyAgainstBaselineUsingFile(compilation); + } + + // [Fact] + // public async Task MapAction_SingleNullableStringParam_WithQueryStringValueProvided_StringReturn() + [Fact] public async Task MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn() { diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/Infrastructure/Bindability.cs b/src/Shared/RoslynUtils/Bindability.cs similarity index 100% rename from src/Framework/AspNetCoreAnalyzers/src/Analyzers/Infrastructure/Bindability.cs rename to src/Shared/RoslynUtils/Bindability.cs diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/Infrastructure/Parsability.cs b/src/Shared/RoslynUtils/Parsability.cs similarity index 100% rename from src/Framework/AspNetCoreAnalyzers/src/Analyzers/Infrastructure/Parsability.cs rename to src/Shared/RoslynUtils/Parsability.cs diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/Infrastructure/ParsabilityHelper.cs b/src/Shared/RoslynUtils/ParsabilityHelper.cs similarity index 85% rename from src/Framework/AspNetCoreAnalyzers/src/Analyzers/Infrastructure/ParsabilityHelper.cs rename to src/Shared/RoslynUtils/ParsabilityHelper.cs index 124832884c96..c23425b8e224 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/Infrastructure/ParsabilityHelper.cs +++ b/src/Shared/RoslynUtils/ParsabilityHelper.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.App.Analyzers.Infrastructure; using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.AspNetCore.Analyzers.Infrastructure; @@ -16,32 +17,41 @@ namespace Microsoft.AspNetCore.Analyzers.Infrastructure; internal static class ParsabilityHelper { - private static bool IsTypeAlwaysParsableOrBindable(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes) + private static bool IsTypeAlwaysParsableOrBindable(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes, [NotNullWhen(true)] out ParsabilityMethod? parsabilityMethod) { // Any enum is valid. if (typeSymbol.TypeKind == TypeKind.Enum) { + parsabilityMethod = ParsabilityMethod.Enum; return true; } // Uri is valid. if (SymbolEqualityComparer.Default.Equals(typeSymbol, wellKnownTypes.Get(WellKnownType.System_Uri))) { + parsabilityMethod = ParsabilityMethod.Uri; return true; } // Strings are valid. if (typeSymbol.SpecialType == SpecialType.System_String) { + parsabilityMethod = ParsabilityMethod.String; return true; } + parsabilityMethod = null; return false; } internal static Parsability GetParsability(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes) { - if (IsTypeAlwaysParsableOrBindable(typeSymbol, wellKnownTypes)) + return GetParsability(typeSymbol, wellKnownTypes, out var _); + } + + internal static Parsability GetParsability(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes, out ParsabilityMethod? parsabilityMethod) + { + if (IsTypeAlwaysParsableOrBindable(typeSymbol, wellKnownTypes, out parsabilityMethod)) { return Parsability.Parsable; } @@ -49,17 +59,23 @@ internal static Parsability GetParsability(ITypeSymbol typeSymbol, WellKnownType // MyType : IParsable() if (IsParsableViaIParsable(typeSymbol, wellKnownTypes)) { + parsabilityMethod = ParsabilityMethod.IParsable; return Parsability.Parsable; } // Check if the parameter type has a public static TryParse method. var tryParseMethods = typeSymbol.GetMembers("TryParse").OfType(); - foreach (var tryParseMethodSymbol in tryParseMethods) + + if (tryParseMethods.Any(m => IsTryParseWithFormat(m, wellKnownTypes))) { - if (IsTryParse(tryParseMethodSymbol) || IsTryParseWithFormat(tryParseMethodSymbol, wellKnownTypes)) - { - return Parsability.Parsable; - } + parsabilityMethod = ParsabilityMethod.TryParseWithFormatProvider; + return Parsability.Parsable; + } + + if (tryParseMethods.Any(IsTryParse)) + { + parsabilityMethod = ParsabilityMethod.TryParse; + return Parsability.Parsable; } return Parsability.NotParsable; @@ -86,7 +102,7 @@ private static bool IsTryParseWithFormat(IMethodSymbol methodSymbol, WellKnownTy methodSymbol.Parameters[2].RefKind == RefKind.Out; } - private static bool IsParsableViaIParsable(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes) + internal static bool IsParsableViaIParsable(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes) { var iParsableTypeSymbol = wellKnownTypes.Get(WellKnownType.System_IParsable_T); var implementsIParsable = typeSymbol.AllInterfaces.Any( @@ -134,7 +150,7 @@ private static bool IsReturningValueTaskOfT(INamedTypeSymbol returnType, INamedT internal static Bindability GetBindability(INamedTypeSymbol typeSymbol, WellKnownTypes wellKnownTypes) { - if (IsTypeAlwaysParsableOrBindable(typeSymbol, wellKnownTypes)) + if (IsTypeAlwaysParsableOrBindable(typeSymbol, wellKnownTypes, out var _)) { return Bindability.Bindable; } diff --git a/src/Shared/RoslynUtils/ParsabilityMethod.cs b/src/Shared/RoslynUtils/ParsabilityMethod.cs new file mode 100644 index 000000000000..e4b9fbe23064 --- /dev/null +++ b/src/Shared/RoslynUtils/ParsabilityMethod.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Analyzers.Infrastructure; + +internal enum ParsabilityMethod +{ + String, + IParsable, + Enum, + TryParse, + TryParseWithFormatProvider, + Uri +} diff --git a/src/Shared/RoslynUtils/SymbolExtensions.cs b/src/Shared/RoslynUtils/SymbolExtensions.cs index 1809be26edc7..7c73a703a73a 100644 --- a/src/Shared/RoslynUtils/SymbolExtensions.cs +++ b/src/Shared/RoslynUtils/SymbolExtensions.cs @@ -11,6 +11,29 @@ namespace Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure; internal static class SymbolExtensions { + public static INamedTypeSymbol? UnwrapTypeSymbol(this ITypeSymbol typeSymbol) + { + INamedTypeSymbol? unwrappedTypeSymbol = null; + + // If it is an array, unwrap it. + if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol) + { + unwrappedTypeSymbol = arrayTypeSymbol.ElementType as INamedTypeSymbol; + } + else if (typeSymbol is INamedTypeSymbol namedTypeSymbol) + { + unwrappedTypeSymbol = namedTypeSymbol; + } + + // If it is nullable, unwrap it. + if (unwrappedTypeSymbol!.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T) + { + unwrappedTypeSymbol = unwrappedTypeSymbol.TypeArguments[0] as INamedTypeSymbol; + } + + return unwrappedTypeSymbol; + } + public static bool HasAttribute(this ISymbol symbol, INamedTypeSymbol attributeType) { foreach (var attributeData in symbol.GetAttributes()) diff --git a/src/Shared/RoslynUtils/WellKnownTypeData.cs b/src/Shared/RoslynUtils/WellKnownTypeData.cs index 8632c5b66f2f..eb8337a24cf8 100644 --- a/src/Shared/RoslynUtils/WellKnownTypeData.cs +++ b/src/Shared/RoslynUtils/WellKnownTypeData.cs @@ -28,6 +28,8 @@ public enum WellKnownType System_Security_Claims_ClaimsPrincipal, Microsoft_AspNetCore_Http_IFormFileCollection, Microsoft_AspNetCore_Http_IFormFile, + System_DateOnly, + System_DateTimeOffset, System_IO_Stream, System_IO_Pipelines_PipeReader, System_IFormatProvider, @@ -129,6 +131,8 @@ public enum WellKnownType "System.Security.Claims.ClaimsPrincipal", "Microsoft.AspNetCore.Http.IFormFileCollection", "Microsoft.AspNetCore.Http.IFormFile", + "System.DateOnly", + "System.DateTimeOffset", "System.IO.Stream", "System.IO.Pipelines.PipeReader", "System.IFormatProvider",