Browse Source

Add initial support for parameters with `AsParameters` attribute (#47914)

Safia Abdalla 2 years ago
parent
commit
ce9e1ae550
60 changed files with 2681 additions and 837 deletions
  1. 7 0
      docs/list-of-diagnostics.md
  2. 1 1
      src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteEmbeddedLanguage/Infrastructure/RoutePatternParametersDetector.cs
  3. 48 0
      src/Http/Http.Extensions/gen/DiagnosticDescriptors.cs
  4. 9 1
      src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs
  5. 96 2
      src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs
  6. 63 27
      src/Http/Http.Extensions/gen/Resources.resx
  7. 1 0
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs
  8. 11 0
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterExtensions.cs
  9. 33 2
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointEmitter.cs
  10. 13 11
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs
  11. 2 17
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Endpoint.cs
  12. 224 67
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs
  13. 3 0
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameterSource.cs
  14. 7 0
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Model/ConstructorParameter.cs
  15. 35 0
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Model/ParameterLookupKey.cs
  16. 22 4
      src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs
  17. 1 1
      src/Http/Http.Extensions/src/RequestDelegateFactory.cs
  18. 1 558
      src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs
  19. 56 20
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt
  20. 8 4
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt
  21. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt
  22. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt
  23. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt
  24. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt
  25. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt
  26. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt
  27. 11 5
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt
  28. 19 9
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt
  29. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt
  30. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt
  31. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt
  32. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt
  33. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt
  34. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt
  35. 4 2
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt
  36. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt
  37. 4 2
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt
  38. 4 2
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt
  39. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt
  40. 4 2
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsValidationProblemResult_Has_Metadata.generated.txt
  41. 4 2
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsVoid_Has_No_Metadata.generated.txt
  42. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt
  43. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt
  44. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt
  45. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt
  46. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt
  47. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt
  48. 10 2
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt
  49. 8 2
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt
  50. 5 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt
  51. 1042 0
      src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt
  52. 106 0
      src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.AsParameters.cs
  53. 1 22
      src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.cs
  54. 420 0
      src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.AsParameters.cs
  55. 2 0
      src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.JsonBody.cs
  56. 3 0
      src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.JsonBodyOrService.cs
  57. 12 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Logging.cs
  58. 224 3
      src/Http/Http.Extensions/test/RequestDelegateGenerator/SharedTypes.cs
  59. 1 1
      src/Shared/PropertyAsParameterInfo.cs
  60. 56 2
      src/Shared/RoslynUtils/SymbolExtensions.cs

+ 7 - 0
docs/list-of-diagnostics.md

@@ -72,6 +72,13 @@
 |  __`RDG002`__ | Unable to resolve endpoint handler |
 |  __`RDG002`__ | Unable to resolve endpoint handler |
 |  __`RDG003`__ | Unable to resolve parameter |
 |  __`RDG003`__ | Unable to resolve parameter |
 |  __`RDG004`__ | Unable to resolve anonymous type |
 |  __`RDG004`__ | Unable to resolve anonymous type |
+|  __`RDG005`__ | Invalid abstract type |
+|  __`RDG006`__ | Invalid constructor parameters |
+|  __`RDG007`__ | No valid constructor found |
+|  __`RDG008`__ | Multiple public constructors found |
+|  __`RDG009`__ | Invalid nested AsParameters |
+|  __`RDG010`__ | Unexpected nullable type |
+
 
 
 ### SignalR Source Generator (`SSG0000-SSG0110`)
 ### SignalR Source Generator (`SSG0000-SSG0110`)
 
 

+ 1 - 1
src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteEmbeddedLanguage/Infrastructure/RoutePatternParametersDetector.cs

@@ -43,7 +43,7 @@ internal static class RoutePatternParametersDetector
         static string ResolveRouteParameterName(ISymbol parameterSymbol, WellKnownTypes wellKnownTypes)
         static string ResolveRouteParameterName(ISymbol parameterSymbol, WellKnownTypes wellKnownTypes)
         {
         {
             var fromRouteMetadata = wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromRouteMetadata);
             var fromRouteMetadata = wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromRouteMetadata);
-            if (!parameterSymbol.HasAttributeImplementingInterface(fromRouteMetadata, out var attributeData))
+            if (!parameterSymbol.TryGetAttributeImplementingInterface(fromRouteMetadata, out var attributeData))
             {
             {
                 return parameterSymbol.Name; // No route metadata attribute!
                 return parameterSymbol.Name; // No route metadata attribute!
             }
             }

+ 48 - 0
src/Http/Http.Extensions/gen/DiagnosticDescriptors.cs

@@ -42,4 +42,52 @@ internal static class DiagnosticDescriptors
         "Usage",
         "Usage",
         DiagnosticSeverity.Warning,
         DiagnosticSeverity.Warning,
         isEnabledByDefault: true);
         isEnabledByDefault: true);
+
+    public static DiagnosticDescriptor InvalidAsParametersAbstractType { get; } = new(
+        "RDG005",
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersAbstractType_Title), Resources.ResourceManager, typeof(Resources)),
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersAbstractType_Message), Resources.ResourceManager, typeof(Resources)),
+        "Usage",
+        DiagnosticSeverity.Error,
+        isEnabledByDefault: true);
+
+    public static DiagnosticDescriptor InvalidAsParametersSignature { get; } = new(
+        "RDG006",
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersSignature_Title), Resources.ResourceManager, typeof(Resources)),
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersSignature_Message), Resources.ResourceManager, typeof(Resources)),
+        "Usage",
+        DiagnosticSeverity.Error,
+        isEnabledByDefault: true);
+
+    public static DiagnosticDescriptor InvalidAsParametersNoConstructorFound { get; } = new(
+        "RDG007",
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersNoConstructorFound_Title), Resources.ResourceManager, typeof(Resources)),
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersNoConstructorFound_Message), Resources.ResourceManager, typeof(Resources)),
+        "Usage",
+        DiagnosticSeverity.Error,
+        isEnabledByDefault: true);
+
+    public static DiagnosticDescriptor InvalidAsParametersSingleConstructorOnly { get; } = new(
+        "RDG008",
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersSingleConstructorOnly_Title), Resources.ResourceManager, typeof(Resources)),
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersSingleConstructorOnly_Message), Resources.ResourceManager, typeof(Resources)),
+        "Usage",
+        DiagnosticSeverity.Error,
+        isEnabledByDefault: true);
+
+    public static DiagnosticDescriptor InvalidAsParametersNested { get; } = new(
+        "RDG009",
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersNested_Title), Resources.ResourceManager, typeof(Resources)),
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersNested_Message), Resources.ResourceManager, typeof(Resources)),
+        "Usage",
+        DiagnosticSeverity.Error,
+        isEnabledByDefault: true);
+
+    public static DiagnosticDescriptor InvalidAsParametersNullable { get; } = new(
+        "RDG010",
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersNullable_Title), Resources.ResourceManager, typeof(Resources)),
+        new LocalizableResourceString(nameof(Resources.InvalidAsParametersNullable_Message), Resources.ResourceManager, typeof(Resources)),
+        "Usage",
+        DiagnosticSeverity.Error,
+        isEnabledByDefault: true);
 }
 }

+ 9 - 1
src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs

@@ -69,11 +69,13 @@ public sealed class RequestDelegateGenerator : IIncrementalGenerator
             codeWriter.EndBlockWithComma();
             codeWriter.EndBlockWithComma();
             codeWriter.WriteLine("(del, options, inferredMetadataResult) =>");
             codeWriter.WriteLine("(del, options, inferredMetadataResult) =>");
             codeWriter.StartBlock();
             codeWriter.StartBlock();
+            codeWriter.WriteLine(@"Debug.Assert(options != null, ""RequestDelegateFactoryOptions not found."");");
+            codeWriter.WriteLine(@"Debug.Assert(options.EndpointBuilder != null, ""EndpointBuilder not found."");");
             codeWriter.WriteLine($"var handler = ({endpoint!.EmitHandlerDelegateType(considerOptionality: true)})del;");
             codeWriter.WriteLine($"var handler = ({endpoint!.EmitHandlerDelegateType(considerOptionality: true)})del;");
             codeWriter.WriteLine("EndpointFilterDelegate? filteredInvocation = null;");
             codeWriter.WriteLine("EndpointFilterDelegate? filteredInvocation = null;");
             if (endpoint!.EmitterContext.RequiresLoggingHelper || endpoint!.EmitterContext.HasJsonBodyOrService || endpoint!.Response?.IsSerializableJsonResponse(out var _) is true)
             if (endpoint!.EmitterContext.RequiresLoggingHelper || endpoint!.EmitterContext.HasJsonBodyOrService || endpoint!.Response?.IsSerializableJsonResponse(out var _) is true)
             {
             {
-                codeWriter.WriteLine("var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;");
+                codeWriter.WriteLine("var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;");
             }
             }
             endpoint!.EmitLoggingPreamble(codeWriter);
             endpoint!.EmitLoggingPreamble(codeWriter);
             endpoint!.EmitRouteOrQueryResolver(codeWriter);
             endpoint!.EmitRouteOrQueryResolver(codeWriter);
@@ -235,6 +237,7 @@ public sealed class RequestDelegateGenerator : IIncrementalGenerator
                 var hasFormBody = endpoints.Any(endpoint => endpoint!.EmitterContext.HasFormBody);
                 var hasFormBody = endpoints.Any(endpoint => endpoint!.EmitterContext.HasFormBody);
                 var hasJsonBody = endpoints.Any(endpoint => endpoint!.EmitterContext.HasJsonBody || endpoint!.EmitterContext.HasJsonBodyOrService);
                 var hasJsonBody = endpoints.Any(endpoint => endpoint!.EmitterContext.HasJsonBody || endpoint!.EmitterContext.HasJsonBodyOrService);
                 var hasResponseMetadata = endpoints.Any(endpoint => endpoint!.EmitterContext.HasResponseMetadata);
                 var hasResponseMetadata = endpoints.Any(endpoint => endpoint!.EmitterContext.HasResponseMetadata);
+                var requiresPropertyAsParameterInfo = endpoints.Any(endpoint => endpoint!.EmitterContext.RequiresPropertyAsParameterInfo);
 
 
                 using var stringWriter = new StringWriter(CultureInfo.InvariantCulture);
                 using var stringWriter = new StringWriter(CultureInfo.InvariantCulture);
                 using var codeWriter = new CodeWriter(stringWriter, baseIndent: 0);
                 using var codeWriter = new CodeWriter(stringWriter, baseIndent: 0);
@@ -254,6 +257,11 @@ public sealed class RequestDelegateGenerator : IIncrementalGenerator
                     codeWriter.WriteLine(RequestDelegateGeneratorSources.ContentTypeConstantsType);
                     codeWriter.WriteLine(RequestDelegateGeneratorSources.ContentTypeConstantsType);
                 }
                 }
 
 
+                if (requiresPropertyAsParameterInfo)
+                {
+                    codeWriter.WriteLine(RequestDelegateGeneratorSources.PropertyAsParameterInfoClass);
+                }
+
                 return stringWriter.ToString();
                 return stringWriter.ToString();
             });
             });
 
 

+ 96 - 2
src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs

@@ -253,7 +253,7 @@ internal static class RequestDelegateGeneratorSources
 """;
 """;
 
 
     public static string LogOrThrowExceptionHelperClass => $$"""
     public static string LogOrThrowExceptionHelperClass => $$"""
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;
@@ -397,13 +397,107 @@ internal static class RequestDelegateGeneratorSources
     }
     }
 """;
 """;
 
 
+    public static string PropertyAsParameterInfoClass = """
+    file sealed class PropertyAsParameterInfo : ParameterInfo
+    {
+        private readonly PropertyInfo _underlyingProperty;
+        private readonly ParameterInfo? _constructionParameterInfo;
+
+        public PropertyAsParameterInfo(bool isOptional, PropertyInfo propertyInfo)
+        {
+            Debug.Assert(propertyInfo != null, "PropertyInfo must be provided.");
+
+            AttrsImpl = (ParameterAttributes)propertyInfo.Attributes;
+            NameImpl = propertyInfo.Name;
+            MemberImpl = propertyInfo;
+            ClassImpl = propertyInfo.PropertyType;
+
+            // It is not a real parameter in the delegate, so,
+            // not defining a real position.
+            PositionImpl = -1;
+
+            _underlyingProperty = propertyInfo;
+            IsOptional = isOptional;
+        }
+
+        public PropertyAsParameterInfo(bool isOptional, PropertyInfo property, ParameterInfo? parameterInfo)
+            : this(isOptional, property)
+        {
+            _constructionParameterInfo = parameterInfo;
+        }
+
+        public override bool HasDefaultValue
+            => _constructionParameterInfo is not null && _constructionParameterInfo.HasDefaultValue;
+        public override object? DefaultValue
+            => _constructionParameterInfo?.DefaultValue;
+        public override int MetadataToken => _underlyingProperty.MetadataToken;
+        public override object? RawDefaultValue
+            => _constructionParameterInfo?.RawDefaultValue;
+
+        public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+        {
+            var attributes = _constructionParameterInfo?.GetCustomAttributes(attributeType, inherit);
+
+            if (attributes == null || attributes is { Length: 0 })
+            {
+                attributes = _underlyingProperty.GetCustomAttributes(attributeType, inherit);
+            }
+
+            return attributes;
+        }
+
+        public override object[] GetCustomAttributes(bool inherit)
+        {
+            var constructorAttributes = _constructionParameterInfo?.GetCustomAttributes(inherit);
+
+            if (constructorAttributes == null || constructorAttributes is { Length: 0 })
+            {
+                return _underlyingProperty.GetCustomAttributes(inherit);
+            }
+
+            var propertyAttributes = _underlyingProperty.GetCustomAttributes(inherit);
+
+            // Since the constructors attributes should take priority we will add them first,
+            // as we usually call it as First() or FirstOrDefault() in the argument creation
+            var mergedAttributes = new object[constructorAttributes.Length + propertyAttributes.Length];
+            Array.Copy(constructorAttributes, mergedAttributes, constructorAttributes.Length);
+            Array.Copy(propertyAttributes, 0, mergedAttributes, constructorAttributes.Length, propertyAttributes.Length);
+
+            return mergedAttributes;
+        }
+
+        public override IList<CustomAttributeData> GetCustomAttributesData()
+        {
+            var attributes = new List<CustomAttributeData>(
+                _constructionParameterInfo?.GetCustomAttributesData() ?? Array.Empty<CustomAttributeData>());
+            attributes.AddRange(_underlyingProperty.GetCustomAttributesData());
+
+            return attributes.AsReadOnly();
+        }
+
+        public override Type[] GetOptionalCustomModifiers()
+            => _underlyingProperty.GetOptionalCustomModifiers();
+
+        public override Type[] GetRequiredCustomModifiers()
+            => _underlyingProperty.GetRequiredCustomModifiers();
+
+        public override bool IsDefined(Type attributeType, bool inherit)
+        {
+            return (_constructionParameterInfo is not null && _constructionParameterInfo.IsDefined(attributeType, inherit)) ||
+                _underlyingProperty.IsDefined(attributeType, inherit);
+        }
+
+        public new bool IsOptional { get; }
+    }
+""";
+
     public static string GetGeneratedRouteBuilderExtensionsSource(string genericThunks, string thunks, string endpoints, string helperMethods, string helperTypes) => $$"""
     public static string GetGeneratedRouteBuilderExtensionsSource(string genericThunks, string thunks, string endpoints, string helperMethods, string helperTypes) => $$"""
 {{SourceHeader}}
 {{SourceHeader}}
 
 
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     {{GeneratedCodeAttribute}}
     {{GeneratedCodeAttribute}}
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }

+ 63 - 27
src/Http/Http.Extensions/gen/Resources.resx

@@ -1,17 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <root>
 <root>
-  <!-- 
-    Microsoft ResX Schema 
-    
+  <!--
+    Microsoft ResX Schema
+
     Version 2.0
     Version 2.0
-    
-    The primary goals of this format is to allow a simple XML format 
-    that is mostly human readable. The generation and parsing of the 
-    various data types are done through the TypeConverter classes 
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
     associated with the data types.
     associated with the data types.
-    
+
     Example:
     Example:
-    
+
     ... ado.net/XML headers & schema ...
     ... ado.net/XML headers & schema ...
     <resheader name="resmimetype">text/microsoft-resx</resheader>
     <resheader name="resmimetype">text/microsoft-resx</resheader>
     <resheader name="version">2.0</resheader>
     <resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
         <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
         <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
         <comment>This is a comment</comment>
         <comment>This is a comment</comment>
     </data>
     </data>
-                
-    There are any number of "resheader" rows that contain simple 
+
+    There are any number of "resheader" rows that contain simple
     name/value pairs.
     name/value pairs.
-    
-    Each data row contains a name, and value. The row also contains a 
-    type or mimetype. Type corresponds to a .NET class that support 
-    text/value conversion through the TypeConverter architecture. 
-    Classes that don't support this are serialized and stored with the 
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
     mimetype set.
     mimetype set.
-    
-    The mimetype is used for serialized objects, and tells the 
-    ResXResourceReader how to depersist the object. This is currently not 
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
     extensible. For a given mimetype the value must be set accordingly:
     extensible. For a given mimetype the value must be set accordingly:
-    
-    Note - application/x-microsoft.net.object.binary.base64 is the format 
-    that the ResXResourceWriter will generate, however the reader can 
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
     read any of the formats listed below.
     read any of the formats listed below.
-    
+
     mimetype: application/x-microsoft.net.object.binary.base64
     mimetype: application/x-microsoft.net.object.binary.base64
-    value   : The object must be serialized with 
+    value   : The object must be serialized with
             : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
             : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
             : and then encoded with base64 encoding.
             : and then encoded with base64 encoding.
-    
+
     mimetype: application/x-microsoft.net.object.soap.base64
     mimetype: application/x-microsoft.net.object.soap.base64
-    value   : The object must be serialized with 
+    value   : The object must be serialized with
             : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
             : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
             : and then encoded with base64 encoding.
             : and then encoded with base64 encoding.
 
 
     mimetype: application/x-microsoft.net.object.bytearray.base64
     mimetype: application/x-microsoft.net.object.bytearray.base64
-    value   : The object must be serialized into a byte array 
+    value   : The object must be serialized into a byte array
             : using a System.ComponentModel.TypeConverter
             : using a System.ComponentModel.TypeConverter
             : and then encoded with base64 encoding.
             : and then encoded with base64 encoding.
     -->
     -->
@@ -141,4 +141,40 @@
   <data name="UnableToResolveAnonymousReturnType_Title" xml:space="preserve">
   <data name="UnableToResolveAnonymousReturnType_Title" xml:space="preserve">
     <value>Unable to resolve anonymous type</value>
     <value>Unable to resolve anonymous type</value>
   </data>
   </data>
+  <data name="InvalidAsParametersAbstractType_Title" xml:space="preserve">
+    <value>Invalid abstract type</value>
+  </data>
+  <data name="InvalidAsParametersAbstractType_Message" xml:space="preserve">
+    <value>The abstract type '{0}' is not supported.</value>
+  </data>
+  <data name="InvalidAsParametersSignature_Title" xml:space="preserve">
+    <value>Invalid constructor parameters</value>
+  </data>
+  <data name="InvalidAsParametersSignature_Message" xml:space="preserve">
+    <value>The public parameterized constructor must contain only parameters that match the declared public properties for type '{0}'.</value>
+  </data>
+  <data name="InvalidAsParametersNoConstructorFound_Title" xml:space="preserve">
+    <value>No valid constructor found</value>
+  </data>
+  <data name="InvalidAsParametersNoConstructorFound_Message" xml:space="preserve">
+    <value>No public parameterless constructor found for type '{0}'.</value>
+  </data>
+  <data name="InvalidAsParametersSingleConstructorOnly_Title" xml:space="preserve">
+    <value>Multiple public constructors found</value>
+  </data>
+  <data name="InvalidAsParametersSingleConstructorOnly_Message" xml:space="preserve">
+    <value>Only a single public parameterized constructor is allowed for type '{0}'.</value>
+  </data>
+  <data name="InvalidAsParametersNested_Title" xml:space="preserve">
+    <value>Invalid nested AsParameters</value>
+  </data>
+  <data name="InvalidAsParametersNested_Message" xml:space="preserve">
+    <value>Nested AsParametersAttribute is not supported and should be used only for handler parameters.</value>
+  </data>
+  <data name="InvalidAsParametersNullable_Title" xml:space="preserve">
+    <value>Unexpected nullable type</value>
+  </data>
+  <data name="InvalidAsParametersNullable_Message" xml:space="preserve">
+    <value>The nullable type '{0}' is not supported. Mark the parameter as non-nullable.</value>
+  </data>
 </root>
 </root>

+ 1 - 0
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterContext.cs

@@ -11,6 +11,7 @@ internal sealed class EmitterContext
     public bool HasBindAsync { get; set; }
     public bool HasBindAsync { get; set; }
     public bool HasParsable { get; set; }
     public bool HasParsable { get; set; }
     public bool HasJsonResponse { get; set; }
     public bool HasJsonResponse { get; set; }
+    public bool RequiresPropertyAsParameterInfo { get; set; }
     public bool RequiresLoggingHelper { get; set; }
     public bool RequiresLoggingHelper { get; set; }
     public bool HasEndpointMetadataProvider { get; set; }
     public bool HasEndpointMetadataProvider { get; set; }
     public bool HasEndpointParameterMetadataProvider { get; set; }
     public bool HasEndpointParameterMetadataProvider { get; set; }

+ 11 - 0
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EmitterExtensions.cs

@@ -1,5 +1,6 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 // The .NET Foundation licenses this file to you under the MIT license.
+using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using Microsoft.AspNetCore.Analyzers.Infrastructure;
 using Microsoft.AspNetCore.Analyzers.Infrastructure;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis;
@@ -12,6 +13,7 @@ internal static class EmitterExtensions
     {
     {
         EndpointParameterSource.Header => "header",
         EndpointParameterSource.Header => "header",
         EndpointParameterSource.Query => "query string",
         EndpointParameterSource.Query => "query string",
+        EndpointParameterSource.Route => "route",
         EndpointParameterSource.RouteOrQuery => "route or query string",
         EndpointParameterSource.RouteOrQuery => "route or query string",
         EndpointParameterSource.FormBody => "form",
         EndpointParameterSource.FormBody => "form",
         EndpointParameterSource.BindAsync => endpointParameter.BindMethod == BindabilityMethod.BindAsync
         EndpointParameterSource.BindAsync => endpointParameter.BindMethod == BindabilityMethod.BindAsync
@@ -30,4 +32,13 @@ internal static class EmitterExtensions
         }
         }
         return false;
         return false;
     }
     }
+
+    public static string EmitHandlerArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.SymbolName}_local";
+
+    public static string EmitArgument(this EndpointParameter endpointParameter) => endpointParameter.Source switch
+    {
+        EndpointParameterSource.JsonBody or EndpointParameterSource.Route or EndpointParameterSource.RouteOrQuery or EndpointParameterSource.JsonBodyOrService or EndpointParameterSource.FormBody => endpointParameter.IsOptional ? endpointParameter.EmitHandlerArgument() : $"{endpointParameter.EmitHandlerArgument()}!",
+        EndpointParameterSource.Unknown => throw new NotImplementedException("Unreachable!"),
+        _ => endpointParameter.EmitHandlerArgument()
+    };
 }
 }

+ 33 - 2
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointEmitter.cs

@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 // The .NET Foundation licenses this file to you under the MIT license.
 
 
+using System.Collections.Generic;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.CodeAnalysis.CSharp;
 using System.Globalization;
 using System.Globalization;
@@ -10,13 +11,13 @@ using System.Linq;
 namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel.Emitters;
 namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel.Emitters;
 internal static class EndpointEmitter
 internal static class EndpointEmitter
 {
 {
-    internal static string EmitParameterPreparation(this Endpoint endpoint, int baseIndent = 0)
+    internal static string EmitParameterPreparation(this IEnumerable<EndpointParameter> endpointParameters, EmitterContext emitterContext, int baseIndent = 0)
     {
     {
         using var stringWriter = new StringWriter(CultureInfo.InvariantCulture);
         using var stringWriter = new StringWriter(CultureInfo.InvariantCulture);
         using var parameterPreparationBuilder = new CodeWriter(stringWriter, baseIndent);
         using var parameterPreparationBuilder = new CodeWriter(stringWriter, baseIndent);
         var readFormEmitted = false;
         var readFormEmitted = false;
 
 
-        foreach (var parameter in endpoint.Parameters)
+        foreach (var parameter in endpointParameters)
         {
         {
             switch (parameter.Source)
             switch (parameter.Source)
             {
             {
@@ -31,9 +32,11 @@ internal static class EndpointEmitter
                     parameter.EmitRouteParameterPreparation(parameterPreparationBuilder);
                     parameter.EmitRouteParameterPreparation(parameterPreparationBuilder);
                     break;
                     break;
                 case EndpointParameterSource.RouteOrQuery:
                 case EndpointParameterSource.RouteOrQuery:
+                    emitterContext.HasRouteOrQuery = true;
                     parameter.EmitRouteOrQueryParameterPreparation(parameterPreparationBuilder);
                     parameter.EmitRouteOrQueryParameterPreparation(parameterPreparationBuilder);
                     break;
                     break;
                 case EndpointParameterSource.BindAsync:
                 case EndpointParameterSource.BindAsync:
+                    emitterContext.HasBindAsync = true;
                     parameter.EmitBindAsyncPreparation(parameterPreparationBuilder);
                     parameter.EmitBindAsyncPreparation(parameterPreparationBuilder);
                     break;
                     break;
                 case EndpointParameterSource.JsonBody:
                 case EndpointParameterSource.JsonBody:
@@ -48,6 +51,9 @@ internal static class EndpointEmitter
                 case EndpointParameterSource.Service:
                 case EndpointParameterSource.Service:
                     parameter.EmitServiceParameterPreparation(parameterPreparationBuilder);
                     parameter.EmitServiceParameterPreparation(parameterPreparationBuilder);
                     break;
                     break;
+                case EndpointParameterSource.AsParameters:
+                    parameter.EmitAsParametersParameterPreparation(parameterPreparationBuilder, emitterContext);
+                    break;
             }
             }
         }
         }
 
 
@@ -57,12 +63,25 @@ internal static class EndpointEmitter
     public static void EmitRouteOrQueryResolver(this Endpoint endpoint, CodeWriter codeWriter)
     public static void EmitRouteOrQueryResolver(this Endpoint endpoint, CodeWriter codeWriter)
     {
     {
         foreach (var parameter in endpoint.Parameters)
         foreach (var parameter in endpoint.Parameters)
+        {
+            ProcessParameter(parameter, codeWriter, endpoint);
+            if (parameter is { Source: EndpointParameterSource.AsParameters, EndpointParameters: {} innerParameters })
+            {
+                foreach (var innerParameter in innerParameters)
+                {
+                    ProcessParameter(innerParameter, codeWriter, endpoint);
+                }
+            }
+        }
+
+        static void ProcessParameter(EndpointParameter parameter, CodeWriter codeWriter, Endpoint endpoint)
         {
         {
             if (parameter.Source == EndpointParameterSource.RouteOrQuery)
             if (parameter.Source == EndpointParameterSource.RouteOrQuery)
             {
             {
                 var parameterName = parameter.SymbolName;
                 var parameterName = parameter.SymbolName;
                 codeWriter.Write($@"var {parameterName}_RouteOrQueryResolver = ");
                 codeWriter.Write($@"var {parameterName}_RouteOrQueryResolver = ");
                 codeWriter.WriteLine($@"GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery(""{parameterName}"", options?.RouteParameterNames);");
                 codeWriter.WriteLine($@"GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery(""{parameterName}"", options?.RouteParameterNames);");
+                endpoint.EmitterContext.HasRouteOrQuery = true;
             }
             }
         }
         }
     }
     }
@@ -71,6 +90,18 @@ internal static class EndpointEmitter
     {
     {
         var serviceProviderEmitted = false;
         var serviceProviderEmitted = false;
         foreach (var parameter in endpoint.Parameters)
         foreach (var parameter in endpoint.Parameters)
+        {
+            ProcessParameter(parameter, codeWriter, ref serviceProviderEmitted);
+            if (parameter is { Source: EndpointParameterSource.AsParameters, EndpointParameters: {} innerParameters })
+            {
+                foreach (var innerParameter in innerParameters)
+                {
+                    ProcessParameter(innerParameter, codeWriter, ref serviceProviderEmitted);
+                }
+            }
+        }
+
+        static void ProcessParameter(EndpointParameter parameter, CodeWriter codeWriter, ref bool serviceProviderEmitted)
         {
         {
             if (parameter.Source == EndpointParameterSource.JsonBodyOrService)
             if (parameter.Source == EndpointParameterSource.JsonBodyOrService)
             {
             {

+ 13 - 11
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs

@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // The .NET Foundation licenses this file to you under the MIT license.
 
 
 using System;
 using System;
-using System.Globalization;
 using Microsoft.AspNetCore.Analyzers.Infrastructure;
 using Microsoft.AspNetCore.Analyzers.Infrastructure;
 using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
 using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis;
@@ -230,14 +229,17 @@ internal static class EndpointParameterEmitter
     {
     {
         var unwrappedType = endpointParameter.Type.UnwrapTypeSymbol(unwrapNullable: true);
         var unwrappedType = endpointParameter.Type.UnwrapTypeSymbol(unwrapNullable: true);
         var unwrappedTypeString = unwrappedType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
         var unwrappedTypeString = unwrappedType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+        var resolveParameterInfo = endpointParameter.IsProperty
+            ? endpointParameter.PropertyAsParameterInfoConstruction
+            : $"parameters[{endpointParameter.Ordinal}]";
 
 
         switch (endpointParameter.BindMethod)
         switch (endpointParameter.BindMethod)
         {
         {
             case BindabilityMethod.IBindableFromHttpContext:
             case BindabilityMethod.IBindableFromHttpContext:
-                codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await BindAsync<{unwrappedTypeString}>(httpContext, parameters[{endpointParameter.Ordinal}]);");
+                codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await BindAsync<{unwrappedTypeString}>(httpContext, {resolveParameterInfo});");
                 break;
                 break;
             case BindabilityMethod.BindAsyncWithParameter:
             case BindabilityMethod.BindAsyncWithParameter:
-                codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await {unwrappedTypeString}.BindAsync(httpContext, parameters[{endpointParameter.Ordinal}]);");
+                codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await {unwrappedTypeString}.BindAsync(httpContext, {resolveParameterInfo});");
                 break;
                 break;
             case BindabilityMethod.BindAsync:
             case BindabilityMethod.BindAsync:
                 codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await {unwrappedTypeString}.BindAsync(httpContext);");
                 codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await {unwrappedTypeString}.BindAsync(httpContext);");
@@ -281,17 +283,17 @@ internal static class EndpointParameterEmitter
         codeWriter.WriteLine($"var {endpointParameter.EmitHandlerArgument()} = {assigningCode};");
         codeWriter.WriteLine($"var {endpointParameter.EmitHandlerArgument()} = {assigningCode};");
     }
     }
 
 
+    internal static void EmitAsParametersParameterPreparation(this EndpointParameter endpointParameter, CodeWriter codeWriter, EmitterContext emitterContext)
+    {
+        codeWriter.WriteLine(endpointParameter.EmitParameterDiagnosticComment());
+        codeWriter.WriteLine(endpointParameter.EndpointParameters?.EmitParameterPreparation(baseIndent: codeWriter.Indent, emitterContext: emitterContext));
+        codeWriter.WriteLine($"var {endpointParameter.EmitHandlerArgument()} = {endpointParameter.AssigningCode};");
+    }
+
     private static string EmitParameterDiagnosticComment(this EndpointParameter endpointParameter) => $"// Endpoint Parameter: {endpointParameter.SymbolName} (Type = {endpointParameter.Type}, IsOptional = {endpointParameter.IsOptional}, IsParsable = {endpointParameter.IsParsable}, IsArray = {endpointParameter.IsArray}, Source = {endpointParameter.Source})";
     private static string EmitParameterDiagnosticComment(this EndpointParameter endpointParameter) => $"// Endpoint Parameter: {endpointParameter.SymbolName} (Type = {endpointParameter.Type}, IsOptional = {endpointParameter.IsOptional}, IsParsable = {endpointParameter.IsParsable}, IsArray = {endpointParameter.IsArray}, Source = {endpointParameter.Source})";
-    private static string EmitHandlerArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.SymbolName}_local";
+
     private static string EmitTempArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.SymbolName}_temp";
     private static string EmitTempArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.SymbolName}_temp";
 
 
     private static string EmitParsedTempArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.SymbolName}_parsed_temp";
     private static string EmitParsedTempArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.SymbolName}_parsed_temp";
     private static string EmitAssigningCodeResult(this EndpointParameter endpointParameter) => $"{endpointParameter.SymbolName}_raw";
     private static string EmitAssigningCodeResult(this EndpointParameter endpointParameter) => $"{endpointParameter.SymbolName}_raw";
-
-    public static string EmitArgument(this EndpointParameter endpointParameter) => endpointParameter.Source switch
-    {
-        EndpointParameterSource.JsonBody or EndpointParameterSource.Route or EndpointParameterSource.RouteOrQuery or EndpointParameterSource.JsonBodyOrService or EndpointParameterSource.FormBody => endpointParameter.IsOptional ? endpointParameter.EmitHandlerArgument() : $"{endpointParameter.EmitHandlerArgument()}!",
-        EndpointParameterSource.Unknown => throw new NotImplementedException($"Unreachable! Unexpected {nameof(EndpointParameterSource)}: {endpointParameter.Source}"),
-        _ => endpointParameter.EmitHandlerArgument()
-    };
 }
 }

+ 2 - 17
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Endpoint.cs

@@ -69,7 +69,6 @@ internal class Endpoint
             switch (parameter.Source)
             switch (parameter.Source)
             {
             {
                 case EndpointParameterSource.BindAsync:
                 case EndpointParameterSource.BindAsync:
-                    IsAwaitable = true;
                     switch (parameter.BindMethod)
                     switch (parameter.BindMethod)
                     {
                     {
                         case BindabilityMethod.IBindableFromHttpContext:
                         case BindabilityMethod.IBindableFromHttpContext:
@@ -78,11 +77,6 @@ internal class Endpoint
                             break;
                             break;
                     }
                     }
                     break;
                     break;
-                case EndpointParameterSource.JsonBody:
-                case EndpointParameterSource.JsonBodyOrService:
-                case EndpointParameterSource.FormBody:
-                    IsAwaitable = true;
-                    break;
                 case EndpointParameterSource.Unknown:
                 case EndpointParameterSource.Unknown:
                     Diagnostics.Add(Diagnostic.Create(
                     Diagnostics.Add(Diagnostic.Create(
                         DiagnosticDescriptors.UnableToResolveParameterDescriptor,
                         DiagnosticDescriptors.UnableToResolveParameterDescriptor,
@@ -96,25 +90,16 @@ internal class Endpoint
 
 
         Parameters = parameters;
         Parameters = parameters;
 
 
-        EmitterContext.HasEndpointParameterMetadataProvider = Parameters.Any(p => p.IsEndpointParameterMetadataProvider);
-        EmitterContext.HasEndpointMetadataProvider = Response!.IsEndpointMetadataProvider || Parameters.Any(p => p.IsEndpointMetadataProvider || p.IsEndpointParameterMetadataProvider);
-
-        EmitterContext.HasJsonBodyOrService = Parameters.Any(parameter => parameter.Source == EndpointParameterSource.JsonBodyOrService);
-        EmitterContext.HasJsonBody = Parameters.Any(parameter => parameter.Source == EndpointParameterSource.JsonBody);
-        EmitterContext.HasFormBody = Parameters.Any(parameter => parameter.Source == EndpointParameterSource.FormBody);
-        EmitterContext.HasRouteOrQuery = Parameters.Any(parameter => parameter.Source == EndpointParameterSource.RouteOrQuery);
-        EmitterContext.HasBindAsync = Parameters.Any(parameter => parameter.Source == EndpointParameterSource.BindAsync);
-        EmitterContext.HasParsable = Parameters.Any(parameter => parameter.IsParsable);
         EmitterContext.RequiresLoggingHelper = !Parameters.All(parameter =>
         EmitterContext.RequiresLoggingHelper = !Parameters.All(parameter =>
             parameter.Source == EndpointParameterSource.SpecialType ||
             parameter.Source == EndpointParameterSource.SpecialType ||
             parameter is { IsArray: true, ElementType.SpecialType: SpecialType.System_String, Source: EndpointParameterSource.Query });
             parameter is { IsArray: true, ElementType.SpecialType: SpecialType.System_String, Source: EndpointParameterSource.Query });
     }
     }
 
 
     public string HttpMethod { get; }
     public string HttpMethod { get; }
-    public bool IsAwaitable { get; }
+    public bool IsAwaitable { get; set; }
     public bool NeedsParameterArray { get; }
     public bool NeedsParameterArray { get; }
     public string? RoutePattern { get; }
     public string? RoutePattern { get; }
-    public EmitterContext EmitterContext { get;  }
+    public EmitterContext EmitterContext { get; }
     public EndpointResponse? Response { get; }
     public EndpointResponse? Response { get; }
     public EndpointParameter[] Parameters { get; } = Array.Empty<EndpointParameter>();
     public EndpointParameter[] Parameters { get; } = Array.Empty<EndpointParameter>();
     public List<Diagnostic> Diagnostics { get; } = new List<Diagnostic>();
     public List<Diagnostic> Diagnostics { get; } = new List<Diagnostic>();

+ 224 - 67
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs

@@ -2,7 +2,10 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // The .NET Foundation licenses this file to you under the MIT license.
 
 
 using System;
 using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
+using System.Linq;
 using System.Text;
 using System.Text;
 using Microsoft.AspNetCore.Analyzers.Infrastructure;
 using Microsoft.AspNetCore.Analyzers.Infrastructure;
 using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
 using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
@@ -15,136 +18,199 @@ namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerM
 
 
 internal class EndpointParameter
 internal class EndpointParameter
 {
 {
-    public EndpointParameter(Endpoint endpoint, IParameterSymbol parameter, WellKnownTypes wellKnownTypes)
+    public EndpointParameter(Endpoint endpoint, IParameterSymbol parameter, WellKnownTypes wellKnownTypes): this(endpoint, parameter.Type, parameter.Name, wellKnownTypes)
     {
     {
-        Type = parameter.Type;
-        SymbolName = parameter.Name;
-        LookupName = parameter.Name; // Default lookup name is same as parameter name (which is a valid C# identifier).
         Ordinal = parameter.Ordinal;
         Ordinal = parameter.Ordinal;
-        Source = EndpointParameterSource.Unknown;
         IsOptional = parameter.IsOptional();
         IsOptional = parameter.IsOptional();
         DefaultValue = parameter.GetDefaultValueString();
         DefaultValue = parameter.GetDefaultValueString();
-        IsArray = TryGetArrayElementType(parameter, out var elementType);
+        ProcessEndpointParameterSource(endpoint, parameter, parameter.GetAttributes(), wellKnownTypes);
+    }
+
+    private EndpointParameter(Endpoint endpoint, IPropertySymbol property, IParameterSymbol? parameter, WellKnownTypes wellKnownTypes) : this(endpoint, property.Type, property.Name, wellKnownTypes)
+    {
+        Ordinal = parameter?.Ordinal ?? 0;
+        IsProperty = true;
+        IsOptional = property.IsOptional() || parameter?.IsOptional() == true;
+        DefaultValue = parameter?.GetDefaultValueString() ?? "null";
+        // Coalesce attributes on the property and attributes on the matching parameter
+        var attributeBuilder = ImmutableArray.CreateBuilder<AttributeData>();
+        attributeBuilder.AddRange(property.GetAttributes());
+        if (parameter is not null)
+        {
+            attributeBuilder.AddRange(parameter.GetAttributes());
+        }
+
+        var propertyInfo = $"typeof({property.ContainingType.ToDisplayString()})!.GetProperty({SymbolDisplay.FormatLiteral(property.Name, true)})!";
+        PropertyAsParameterInfoConstruction = parameter is not null
+            ? $"new PropertyAsParameterInfo({(IsOptional ? "true" : "false")}, {propertyInfo}, {parameter.GetParameterInfoFromConstructorCode()})"
+            : $"new PropertyAsParameterInfo({(IsOptional ? "true" : "false")}, {propertyInfo})";
+        endpoint.EmitterContext.RequiresPropertyAsParameterInfo = IsProperty && IsEndpointParameterMetadataProvider;
+        ProcessEndpointParameterSource(endpoint, property, attributeBuilder.ToImmutable(), wellKnownTypes);
+    }
+
+    private EndpointParameter(Endpoint endpoint, ITypeSymbol typeSymbol, string parameterName, WellKnownTypes wellKnownTypes)
+    {
+        Type = typeSymbol;
+        SymbolName = parameterName;
+        LookupName = parameterName;
+        Source = EndpointParameterSource.Unknown;
+        IsArray = TryGetArrayElementType(typeSymbol, out var elementType);
         ElementType = elementType;
         ElementType = elementType;
-        IsEndpointMetadataProvider = ImplementsIEndpointMetadataProvider(parameter, wellKnownTypes);
-        IsEndpointParameterMetadataProvider = ImplementsIEndpointParameterMetadataProvider(parameter, wellKnownTypes);
+        IsEndpointMetadataProvider = ImplementsIEndpointMetadataProvider(typeSymbol, wellKnownTypes);
+        IsEndpointParameterMetadataProvider = ImplementsIEndpointParameterMetadataProvider(typeSymbol, wellKnownTypes);
+        endpoint.EmitterContext.HasEndpointParameterMetadataProvider = IsEndpointParameterMetadataProvider;
+        endpoint.EmitterContext.HasEndpointMetadataProvider |= IsEndpointMetadataProvider;
+    }
 
 
-        if (parameter.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromRouteMetadata), out var fromRouteAttribute))
+    private void ProcessEndpointParameterSource(Endpoint endpoint, ISymbol symbol, ImmutableArray<AttributeData> attributes, WellKnownTypes wellKnownTypes)
+    {
+        if (attributes.TryGetAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromRouteMetadata), out var fromRouteAttribute))
         {
         {
             Source = EndpointParameterSource.Route;
             Source = EndpointParameterSource.Route;
-            LookupName = GetEscapedParameterName(fromRouteAttribute, parameter.Name);
-            IsParsable = TryGetParsability(parameter, wellKnownTypes, out var parsingBlockEmitter);
+            LookupName = GetEscapedParameterName(fromRouteAttribute, symbol.Name);
+            IsParsable = TryGetParsability(Type, wellKnownTypes, out var parsingBlockEmitter);
             ParsingBlockEmitter = parsingBlockEmitter;
             ParsingBlockEmitter = parsingBlockEmitter;
         }
         }
-        else if (parameter.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromQueryMetadata), out var fromQueryAttribute))
+        else if (attributes.TryGetAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromQueryMetadata), out var fromQueryAttribute))
         {
         {
             Source = EndpointParameterSource.Query;
             Source = EndpointParameterSource.Query;
-            LookupName = GetEscapedParameterName(fromQueryAttribute, parameter.Name);
-            IsParsable = TryGetParsability(parameter, wellKnownTypes, out var parsingBlockEmitter);
+            LookupName = GetEscapedParameterName(fromQueryAttribute, symbol.Name);
+            IsParsable = TryGetParsability(Type, wellKnownTypes, out var parsingBlockEmitter);
             ParsingBlockEmitter = parsingBlockEmitter;
             ParsingBlockEmitter = parsingBlockEmitter;
         }
         }
-        else if (parameter.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromHeaderMetadata), out var fromHeaderAttribute))
+        else if (attributes.TryGetAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromHeaderMetadata), out var fromHeaderAttribute))
         {
         {
             Source = EndpointParameterSource.Header;
             Source = EndpointParameterSource.Header;
-            LookupName = GetEscapedParameterName(fromHeaderAttribute, parameter.Name);
-            IsParsable = TryGetParsability(parameter, wellKnownTypes, out var parsingBlockEmitter);
+            LookupName = GetEscapedParameterName(fromHeaderAttribute, symbol.Name);
+            IsParsable = TryGetParsability(Type, wellKnownTypes, out var parsingBlockEmitter);
             ParsingBlockEmitter = parsingBlockEmitter;
             ParsingBlockEmitter = parsingBlockEmitter;
         }
         }
-        else if (parameter.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromFormMetadata), out var fromFormAttribute))
+        else if (attributes.TryGetAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromFormMetadata), out var fromFormAttribute))
         {
         {
+            endpoint.IsAwaitable = true;
             Source = EndpointParameterSource.FormBody;
             Source = EndpointParameterSource.FormBody;
-            LookupName = GetEscapedParameterName(fromFormAttribute, parameter.Name);
-            if (SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection)))
+            LookupName = GetEscapedParameterName(fromFormAttribute, symbol.Name);
+            if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection)))
             {
             {
                 IsFormFile = true;
                 IsFormFile = true;
                 AssigningCode = "httpContext.Request.Form.Files";
                 AssigningCode = "httpContext.Request.Form.Files";
             }
             }
-            else if (SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormFile)))
+            else if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormFile)))
             {
             {
                 IsFormFile = true;
                 IsFormFile = true;
                 AssigningCode = $"httpContext.Request.Form.Files[{SymbolDisplay.FormatLiteral(LookupName, true)}]";
                 AssigningCode = $"httpContext.Request.Form.Files[{SymbolDisplay.FormatLiteral(LookupName, true)}]";
             }
             }
-            else if (SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormCollection)))
+            else if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormCollection)))
             {
             {
                 AssigningCode = "httpContext.Request.Form";
                 AssigningCode = "httpContext.Request.Form";
             }
             }
             else
             else
             {
             {
                 AssigningCode = $"(string?)httpContext.Request.Form[{SymbolDisplay.FormatLiteral(LookupName, true)}]";
                 AssigningCode = $"(string?)httpContext.Request.Form[{SymbolDisplay.FormatLiteral(LookupName, true)}]";
-                IsParsable = TryGetParsability(parameter, wellKnownTypes, out var parsingBlockEmitter);
+                IsParsable = TryGetParsability(Type, wellKnownTypes, out var parsingBlockEmitter);
                 ParsingBlockEmitter = parsingBlockEmitter;
                 ParsingBlockEmitter = parsingBlockEmitter;
             }
             }
         }
         }
-        else if (TryGetExplicitFromJsonBody(parameter, wellKnownTypes, out var isOptional))
+        else if (TryGetExplicitFromJsonBody(symbol, attributes, wellKnownTypes, out var isOptional))
         {
         {
-            if (SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.System_IO_Stream)))
+            if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.System_IO_Stream)))
             {
             {
                 Source = EndpointParameterSource.SpecialType;
                 Source = EndpointParameterSource.SpecialType;
                 AssigningCode = "httpContext.Request.Body";
                 AssigningCode = "httpContext.Request.Body";
             }
             }
-            else if (SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.System_IO_Pipelines_PipeReader)))
+            else if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.System_IO_Pipelines_PipeReader)))
             {
             {
                 Source = EndpointParameterSource.SpecialType;
                 Source = EndpointParameterSource.SpecialType;
                 AssigningCode = "httpContext.Request.BodyReader";
                 AssigningCode = "httpContext.Request.BodyReader";
             }
             }
             else
             else
             {
             {
+                endpoint.IsAwaitable = true;
                 Source = EndpointParameterSource.JsonBody;
                 Source = EndpointParameterSource.JsonBody;
             }
             }
             IsOptional = isOptional;
             IsOptional = isOptional;
         }
         }
-        else if (parameter.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata)))
+        else if (attributes.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromServiceMetadata)))
         {
         {
             Source = EndpointParameterSource.Service;
             Source = EndpointParameterSource.Service;
         }
         }
-        else if (parameter.HasAttribute(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_AsParametersAttribute)))
+        else if (attributes.HasAttribute(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_AsParametersAttribute)))
         {
         {
-            Source = EndpointParameterSource.Unknown;
+            Source = EndpointParameterSource.AsParameters;
+            var location = endpoint.Operation.Syntax.GetLocation();
+            if (IsOptional)
+            {
+                endpoint.Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidAsParametersNullable, location, Type.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat)));
+            }
+            if (symbol is IPropertySymbol ||
+                Type is not INamedTypeSymbol namedTypeSymbol ||
+                !TryGetAsParametersConstructor(endpoint, namedTypeSymbol, out var isDefaultConstructor, out var matchedProperties))
+            {
+                if (symbol is IPropertySymbol)
+                {
+                    endpoint.Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidAsParametersNested, location));
+                }
+                return;
+            }
+            EndpointParameters = matchedProperties.Select(matchedParameter => new EndpointParameter(endpoint, matchedParameter.Property, matchedParameter.Parameter, wellKnownTypes));
+            if (isDefaultConstructor == true)
+            {
+                var parameterList = string.Join(", ", EndpointParameters.Select(p => $"{p.LookupName} = {p.EmitHandlerArgument()}"));
+                AssigningCode = $"new {namedTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {{ {parameterList} }}";
+            }
+            else
+            {
+                var parameterList = string.Join(", ", EndpointParameters.Select(p => p.EmitHandlerArgument()));
+                AssigningCode = $"new {namedTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}({parameterList})";
+            }
         }
         }
         else if (TryGetSpecialTypeAssigningCode(Type, wellKnownTypes, out var specialTypeAssigningCode))
         else if (TryGetSpecialTypeAssigningCode(Type, wellKnownTypes, out var specialTypeAssigningCode))
         {
         {
             Source = EndpointParameterSource.SpecialType;
             Source = EndpointParameterSource.SpecialType;
             AssigningCode = specialTypeAssigningCode;
             AssigningCode = specialTypeAssigningCode;
         }
         }
-        else if (SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection)))
+        else if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormFileCollection)))
         {
         {
+            endpoint.IsAwaitable = true;
             Source = EndpointParameterSource.FormBody;
             Source = EndpointParameterSource.FormBody;
             IsFormFile = true;
             IsFormFile = true;
-            LookupName = parameter.Name;
             AssigningCode = "httpContext.Request.Form.Files";
             AssigningCode = "httpContext.Request.Form.Files";
         }
         }
-        else if (SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormFile)))
+        else if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormFile)))
         {
         {
+            endpoint.IsAwaitable = true;
             Source = EndpointParameterSource.FormBody;
             Source = EndpointParameterSource.FormBody;
             IsFormFile = true;
             IsFormFile = true;
-            LookupName = parameter.Name;
             AssigningCode = $"httpContext.Request.Form.Files[{SymbolDisplay.FormatLiteral(LookupName, true)}]";
             AssigningCode = $"httpContext.Request.Form.Files[{SymbolDisplay.FormatLiteral(LookupName, true)}]";
         }
         }
-        else if (SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormCollection)))
+        else if (SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_IFormCollection)))
         {
         {
+            endpoint.IsAwaitable = true;
             Source = EndpointParameterSource.FormBody;
             Source = EndpointParameterSource.FormBody;
-            LookupName = parameter.Name;
+            LookupName = symbol.Name;
             AssigningCode = "httpContext.Request.Form";
             AssigningCode = "httpContext.Request.Form";
         }
         }
-        else if (HasBindAsync(parameter, wellKnownTypes, out var bindMethod))
+        else if (HasBindAsync(Type, wellKnownTypes, out var bindMethod))
         {
         {
+            endpoint.IsAwaitable = true;
+            endpoint.EmitterContext.RequiresPropertyAsParameterInfo = IsProperty && bindMethod is BindabilityMethod.BindAsyncWithParameter or BindabilityMethod.IBindableFromHttpContext;
             Source = EndpointParameterSource.BindAsync;
             Source = EndpointParameterSource.BindAsync;
             BindMethod = bindMethod;
             BindMethod = bindMethod;
         }
         }
-        else if (parameter.Type.SpecialType == SpecialType.System_String)
+        else if (Type.SpecialType == SpecialType.System_String)
         {
         {
             Source = EndpointParameterSource.RouteOrQuery;
             Source = EndpointParameterSource.RouteOrQuery;
         }
         }
-        else if (ShouldDisableInferredBodyParameters(endpoint.HttpMethod) && IsArray && elementType.SpecialType == SpecialType.System_String)
+        else if (ShouldDisableInferredBodyParameters(endpoint.HttpMethod) && IsArray && ElementType.SpecialType == SpecialType.System_String)
         {
         {
             Source = EndpointParameterSource.Query;
             Source = EndpointParameterSource.Query;
         }
         }
-        else if (ShouldDisableInferredBodyParameters(endpoint.HttpMethod) && SymbolEqualityComparer.Default.Equals(parameter.Type, wellKnownTypes.Get(WellKnownType.Microsoft_Extensions_Primitives_StringValues)))
+        else if (ShouldDisableInferredBodyParameters(endpoint.HttpMethod) && SymbolEqualityComparer.Default.Equals(Type, wellKnownTypes.Get(WellKnownType.Microsoft_Extensions_Primitives_StringValues)))
         {
         {
             Source = EndpointParameterSource.Query;
             Source = EndpointParameterSource.Query;
             IsStringValues = true;
             IsStringValues = true;
         }
         }
-        else if (TryGetParsability(parameter, wellKnownTypes, out var parsingBlockEmitter))
+        else if (TryGetParsability(Type, wellKnownTypes, out var parsingBlockEmitter))
         {
         {
             Source = EndpointParameterSource.RouteOrQuery;
             Source = EndpointParameterSource.RouteOrQuery;
             IsParsable = true;
             IsParsable = true;
@@ -152,15 +218,22 @@ internal class EndpointParameter
         }
         }
         else
         else
         {
         {
+            endpoint.IsAwaitable = true;
             Source = EndpointParameterSource.JsonBodyOrService;
             Source = EndpointParameterSource.JsonBodyOrService;
         }
         }
+        endpoint.EmitterContext.HasParsable |= IsParsable;
+        // Set emitter context state for parameters that need to populate
+        // accepts metadata here since we know that will always be required
+        endpoint.EmitterContext.HasFormBody |= Source == EndpointParameterSource.FormBody;
+        endpoint.EmitterContext.HasJsonBody |= Source == EndpointParameterSource.JsonBody;
+        endpoint.EmitterContext.HasJsonBodyOrService |= Source == EndpointParameterSource.JsonBodyOrService;
     }
     }
 
 
-    private static bool ImplementsIEndpointMetadataProvider(IParameterSymbol parameter, WellKnownTypes wellKnownTypes)
-        => parameter.Type.Implements(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IEndpointMetadataProvider));
+    private static bool ImplementsIEndpointMetadataProvider(ITypeSymbol type, WellKnownTypes wellKnownTypes)
+        => type.Implements(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IEndpointMetadataProvider));
 
 
-    private static bool ImplementsIEndpointParameterMetadataProvider(IParameterSymbol parameter, WellKnownTypes wellKnownTypes)
-        => parameter.Type.Implements(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IEndpointParameterMetadataProvider));
+    private static bool ImplementsIEndpointParameterMetadataProvider(ITypeSymbol type, WellKnownTypes wellKnownTypes)
+        => type.Implements(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IEndpointParameterMetadataProvider));
 
 
     private static bool ShouldDisableInferredBodyParameters(string httpMethod)
     private static bool ShouldDisableInferredBodyParameters(string httpMethod)
     {
     {
@@ -178,37 +251,40 @@ internal class EndpointParameter
     public bool IsEndpointMetadataProvider { get; }
     public bool IsEndpointMetadataProvider { get; }
     public bool IsEndpointParameterMetadataProvider { get; }
     public bool IsEndpointParameterMetadataProvider { get; }
     public string SymbolName { get; }
     public string SymbolName { get; }
-    public string LookupName { get; }
+    public string LookupName { get; set; }
     public int Ordinal { get; }
     public int Ordinal { get; }
-    public bool IsOptional { get; }
+    public bool IsOptional { get; set; }
     public bool IsArray { get; set; }
     public bool IsArray { get; set; }
-    public string DefaultValue { get; set; }
-
-    public EndpointParameterSource Source { get; }
-    public bool IsFormFile { get; }
+    public string DefaultValue { get; set; } = "null";
+    [MemberNotNullWhen(true, nameof(PropertyAsParameterInfoConstruction))]
+    public bool IsProperty { get; set; }
+    public EndpointParameterSource Source { get; set; }
+    public string? PropertyAsParameterInfoConstruction { get; set; }
+    public IEnumerable<EndpointParameter>? EndpointParameters { get; set; }
+    public bool IsFormFile { get; set; }
 
 
     // Only used for SpecialType parameters that need
     // Only used for SpecialType parameters that need
     // to be resolved by a specific WellKnownType
     // to be resolved by a specific WellKnownType
-    public string? AssigningCode { get; }
+    public string? AssigningCode { get; set; }
 
 
     [MemberNotNullWhen(true, nameof(ParsingBlockEmitter))]
     [MemberNotNullWhen(true, nameof(ParsingBlockEmitter))]
-    public bool IsParsable { get; }
-    public Action<CodeWriter, string, string>? ParsingBlockEmitter { get; }
-    public bool IsStringValues { get; }
+    public bool IsParsable { get; set; }
+    public Action<CodeWriter, string, string>? ParsingBlockEmitter { get; set; }
+    public bool IsStringValues { get; set; }
 
 
-    public BindabilityMethod? BindMethod { get; }
+    public BindabilityMethod? BindMethod { get; set; }
 
 
-    private static bool HasBindAsync(IParameterSymbol parameter, WellKnownTypes wellKnownTypes, [NotNullWhen(true)] out BindabilityMethod? bindMethod)
+    private static bool HasBindAsync(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes, [NotNullWhen(true)] out BindabilityMethod? bindMethod)
     {
     {
-        var parameterType = parameter.Type.UnwrapTypeSymbol(unwrapArray: true, unwrapNullable: true);
+        var parameterType = typeSymbol.UnwrapTypeSymbol(unwrapArray: true, unwrapNullable: true);
         return ParsabilityHelper.GetBindability(parameterType, wellKnownTypes, out bindMethod) == Bindability.Bindable;
         return ParsabilityHelper.GetBindability(parameterType, wellKnownTypes, out bindMethod) == Bindability.Bindable;
     }
     }
 
 
-    private static bool TryGetArrayElementType(IParameterSymbol parameter, [NotNullWhen(true)]out ITypeSymbol elementType)
+    private static bool TryGetArrayElementType(ITypeSymbol type, [NotNullWhen(true)]out ITypeSymbol elementType)
     {
     {
-        if (parameter.Type.TypeKind == TypeKind.Array)
+        if (type.TypeKind == TypeKind.Array)
         {
         {
-            elementType = parameter.Type.UnwrapTypeSymbol(unwrapArray: true, unwrapNullable: false);
+            elementType = type.UnwrapTypeSymbol(unwrapArray: true, unwrapNullable: false);
             return true;
             return true;
         }
         }
         else
         else
@@ -218,9 +294,9 @@ internal class EndpointParameter
         }
         }
     }
     }
 
 
-    private bool TryGetParsability(IParameterSymbol parameter, WellKnownTypes wellKnownTypes, [NotNullWhen(true)] out Action<CodeWriter, string, string>? parsingBlockEmitter)
+    private bool TryGetParsability(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes, [NotNullWhen(true)] out Action<CodeWriter, string, string>? parsingBlockEmitter)
     {
     {
-        var parameterType = parameter.Type.UnwrapTypeSymbol(unwrapArray: true, unwrapNullable: true);
+        var parameterType = typeSymbol.UnwrapTypeSymbol(unwrapArray: true, unwrapNullable: true);
 
 
         // ParsabilityHelper returns a single enumeration with a Parsable/NonParsable enumeration result. We use this already
         // 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<T>. To
         // in the analyzers to determine whether we need to warn on whether a type needs to implement TryParse/IParsable<T>. To
@@ -276,7 +352,7 @@ internal class EndpointParameter
         {
         {
             parsingBlockEmitter = (writer, inputArgument, outputArgument) =>
             parsingBlockEmitter = (writer, inputArgument, outputArgument) =>
             {
             {
-                writer.WriteLine($"""{parameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {outputArgument} = default;""");
+                writer.WriteLine($"""{typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {outputArgument} = default;""");
                 writer.WriteLine($$"""if ({{preferredTryParseInvocation(inputArgument, $"{inputArgument}_parsed_non_nullable")}})""");
                 writer.WriteLine($$"""if ({{preferredTryParseInvocation(inputArgument, $"{inputArgument}_parsed_non_nullable")}})""");
                 writer.StartBlock();
                 writer.StartBlock();
                 writer.WriteLine($$"""{{outputArgument}} = {{$"{inputArgument}_parsed_non_nullable"}};""");
                 writer.WriteLine($$"""{{outputArgument}} = {{$"{inputArgument}_parsed_non_nullable"}};""");
@@ -324,7 +400,6 @@ internal class EndpointParameter
         return true;
         return true;
     }
     }
 
 
-    // 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)
     private static bool TryGetSpecialTypeAssigningCode(ITypeSymbol type, WellKnownTypes wellKnownTypes, [NotNullWhen(true)] out string? callingCode)
     {
     {
         callingCode = null;
         callingCode = null;
@@ -367,18 +442,26 @@ internal class EndpointParameter
         return false;
         return false;
     }
     }
 
 
-    private static bool TryGetExplicitFromJsonBody(IParameterSymbol parameter,
+    private static bool TryGetExplicitFromJsonBody(ISymbol typeSymbol,
+        ImmutableArray<AttributeData> attributes,
         WellKnownTypes wellKnownTypes,
         WellKnownTypes wellKnownTypes,
         out bool isOptional)
         out bool isOptional)
     {
     {
         isOptional = false;
         isOptional = false;
-        if (!parameter.HasAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromBodyMetadata), out var fromBodyAttribute))
+        if (!attributes.TryGetAttributeImplementingInterface(wellKnownTypes.Get(WellKnownType.Microsoft_AspNetCore_Http_Metadata_IFromBodyMetadata), out var fromBodyAttribute))
         {
         {
             return false;
             return false;
         }
         }
         isOptional |= fromBodyAttribute.TryGetNamedArgumentValue<int>("EmptyBodyBehavior", out var emptyBodyBehaviorValue) && emptyBodyBehaviorValue == 1;
         isOptional |= fromBodyAttribute.TryGetNamedArgumentValue<int>("EmptyBodyBehavior", out var emptyBodyBehaviorValue) && emptyBodyBehaviorValue == 1;
         isOptional |= fromBodyAttribute.TryGetNamedArgumentValue<bool>("AllowEmpty", out var allowEmptyValue) && allowEmptyValue;
         isOptional |= fromBodyAttribute.TryGetNamedArgumentValue<bool>("AllowEmpty", out var allowEmptyValue) && allowEmptyValue;
-        isOptional |= (parameter.NullableAnnotation == NullableAnnotation.Annotated || parameter.HasExplicitDefaultValue);
+        if (typeSymbol is IParameterSymbol parameter)
+        {
+            isOptional |= parameter.IsOptional();
+        }
+        else if (typeSymbol is IPropertySymbol property)
+        {
+            isOptional |= property.IsOptional();
+        }
         return true;
         return true;
     }
     }
 
 
@@ -394,6 +477,80 @@ internal class EndpointParameter
         }
         }
     }
     }
 
 
+    private static bool TryGetAsParametersConstructor(Endpoint endpoint, INamedTypeSymbol type, out bool? isDefaultConstructor, [NotNullWhen(true)] out IEnumerable<ConstructorParameter>? matchedProperties)
+    {
+        isDefaultConstructor = null;
+        matchedProperties = null;
+        var parameterTypeString = type.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat);
+        var location = endpoint.Operation.Syntax.GetLocation();
+        if (type.IsAbstract)
+        {
+            endpoint.Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidAsParametersAbstractType, location, parameterTypeString));
+            return false;
+        }
+
+        var constructors = type.Constructors.Where(constructor => constructor.DeclaredAccessibility == Accessibility.Public && !constructor.IsStatic);
+        var numOfConstructors = constructors.Count();
+        // When leveraging parameterless constructors, we want to ensure we only emit for writable
+        // properties. We do not have this constraint if we are leveraging a parameterized constructor.
+        var properties = type.GetMembers().OfType<IPropertySymbol>().Where(property => property.DeclaredAccessibility == Accessibility.Public);
+        var writableProperties = properties.Where(property => !property.IsReadOnly);
+
+        if (numOfConstructors == 1)
+        {
+            var targetConstructor = constructors.Single();
+            var lookupTable = new Dictionary<ParameterLookupKey, IPropertySymbol>();
+            foreach (var property in properties)
+            {
+                lookupTable.Add(new ParameterLookupKey(property.Name, property.Type), property);
+            }
+
+            var parameters = targetConstructor.GetParameters();
+            var propertiesWithParameterInfo = new List<ConstructorParameter>();
+
+            if (parameters.Length == 0)
+            {
+                isDefaultConstructor = true;
+                matchedProperties = writableProperties.Select(property => new ConstructorParameter(property, null));
+                return true;
+            }
+
+            foreach (var parameter in parameters)
+            {
+                var key = new ParameterLookupKey(parameter.Name!, parameter.Type);
+                if (lookupTable.TryGetValue(key, out var property))
+                {
+                    propertiesWithParameterInfo.Add(new ConstructorParameter(property, parameter));
+                }
+                else
+                {
+                    endpoint.Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidAsParametersSignature, location, parameterTypeString));
+                    return false;
+                }
+            }
+
+            isDefaultConstructor = false;
+            matchedProperties = propertiesWithParameterInfo;
+            return true;
+        }
+
+        if (type.IsValueType)
+        {
+            isDefaultConstructor = true;
+            matchedProperties = writableProperties.Select(property => new ConstructorParameter(property, null));
+            return true;
+        }
+
+        if (numOfConstructors > 1)
+        {
+            endpoint.Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidAsParametersSingleConstructorOnly, location, parameterTypeString));
+            return false;
+        }
+
+        endpoint.Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidAsParametersNoConstructorFound, location, parameterTypeString));
+        return false;
+    }
+
     // Lifted from:
     // Lifted from:
     // https://github.com/dotnet/runtime/blob/dc5a6c8be1644915c14c4a464447b0d54e223a46/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs#L562
     // https://github.com/dotnet/runtime/blob/dc5a6c8be1644915c14c4a464447b0d54e223a46/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs#L562
     private static string ConvertEndOfLineAndQuotationCharactersToEscapeForm(string s)
     private static string ConvertEndOfLineAndQuotationCharactersToEscapeForm(string s)

+ 3 - 0
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameterSource.cs

@@ -20,4 +20,7 @@ internal enum EndpointParameterSource
     BindAsync,
     BindAsync,
     // Unknown should be temporary for development.
     // Unknown should be temporary for development.
     Unknown,
     Unknown,
+    // Used to track that the parameter is annotated with `AsParameters` and
+    // can explode to multiple parameters
+    AsParameters,
 }
 }

+ 7 - 0
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Model/ConstructorParameter.cs

@@ -0,0 +1,7 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel;
+
+internal record ConstructorParameter(IPropertySymbol Property, IParameterSymbol? Parameter);

+ 35 - 0
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Model/ParameterLookupKey.cs

@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel;
+
+internal sealed class ParameterLookupKey
+{
+    public ParameterLookupKey(string name, ITypeSymbol type)
+    {
+        Name = name;
+        Type = type;
+    }
+
+    public string Name { get; }
+    public ITypeSymbol Type { get; }
+
+    public override int GetHashCode()
+    {
+        return StringComparer.OrdinalIgnoreCase.GetHashCode(Name);
+    }
+
+    public override bool Equals([NotNullWhen(true)] object? obj)
+    {
+        if (obj is ParameterLookupKey other)
+        {
+            return SymbolEqualityComparer.Default.Equals(Type, other.Type) &&
+                string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase);
+        }
+        return false;
+    }
+}

+ 22 - 4
src/Http/Http.Extensions/gen/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs

@@ -64,7 +64,7 @@ internal static class StaticRouteHandlerModelEmitter
 
 
         if (endpoint.Parameters.Length > 0)
         if (endpoint.Parameters.Length > 0)
         {
         {
-            codeWriter.WriteLine(endpoint.EmitParameterPreparation(codeWriter.Indent));
+            codeWriter.WriteLine(endpoint.Parameters.EmitParameterPreparation(endpoint.EmitterContext, codeWriter.Indent));
         }
         }
 
 
         codeWriter.WriteLine("if (wasParamCheckFailure)");
         codeWriter.WriteLine("if (wasParamCheckFailure)");
@@ -153,7 +153,7 @@ internal static class StaticRouteHandlerModelEmitter
 
 
         if (endpoint.Parameters.Length > 0)
         if (endpoint.Parameters.Length > 0)
         {
         {
-            codeWriter.WriteLine(endpoint.EmitParameterPreparation(codeWriter.Indent));
+            codeWriter.WriteLine(endpoint.Parameters.EmitParameterPreparation(endpoint.EmitterContext, codeWriter.Indent));
         }
         }
 
 
         codeWriter.WriteLine("if (wasParamCheckFailure)");
         codeWriter.WriteLine("if (wasParamCheckFailure)");
@@ -207,15 +207,33 @@ internal static class StaticRouteHandlerModelEmitter
         }
         }
 
 
         foreach (var parameter in endpoint.Parameters)
         foreach (var parameter in endpoint.Parameters)
+        {
+            if (parameter is { Source: EndpointParameterSource.AsParameters, EndpointParameters: { } innerParameters })
+            {
+                foreach (var innerParameter in innerParameters)
+                {
+                    ProcessParameter(innerParameter, codeWriter);
+                }
+            }
+            else
+            {
+                ProcessParameter(parameter, codeWriter);
+            }
+        }
+
+        static void ProcessParameter(EndpointParameter parameter, CodeWriter codeWriter)
         {
         {
             if (parameter.Type is not { } parameterType)
             if (parameter.Type is not { } parameterType)
             {
             {
-                break;
+                return;
             }
             }
 
 
             if (parameter.IsEndpointParameterMetadataProvider)
             if (parameter.IsEndpointParameterMetadataProvider)
             {
             {
-                codeWriter.WriteLine($$"""var {{parameter.SymbolName}}_ParameterInfo = parameterInfos[{{parameter.Ordinal}}];""");
+                var resolveParameterInfo = parameter.IsProperty
+                    ? parameter.PropertyAsParameterInfoConstruction
+                    : $"parameterInfos[{parameter.Ordinal}]";
+                codeWriter.WriteLine($"var {parameter.SymbolName}_ParameterInfo = {resolveParameterInfo};");
                 codeWriter.WriteLine($"PopulateMetadataForParameter<{parameterType.ToDisplayString(EmitterConstants.DisplayFormat)}>({parameter.SymbolName}_ParameterInfo, options.EndpointBuilder);");
                 codeWriter.WriteLine($"PopulateMetadataForParameter<{parameterType.ToDisplayString(EmitterConstants.DisplayFormat)}>({parameter.SymbolName}_ParameterInfo, options.EndpointBuilder);");
             }
             }
 
 

+ 1 - 1
src/Http/Http.Extensions/src/RequestDelegateFactory.cs

@@ -1495,7 +1495,7 @@ public static partial class RequestDelegateFactory
             for (var i = 0; i < properties.Length; i++)
             for (var i = 0; i < properties.Length; i++)
             {
             {
                 // For parameterless ctor we will init only writable properties.
                 // For parameterless ctor we will init only writable properties.
-                if (properties[i].CanWrite)
+                if (properties[i].CanWrite && properties[i].GetSetMethod(nonPublic: false) != null)
                 {
                 {
                     var parameterInfo = new PropertyAsParameterInfo(properties[i], factoryContext.NullabilityContext);
                     var parameterInfo = new PropertyAsParameterInfo(properties[i], factoryContext.NullabilityContext);
                     bindings.Add(Expression.Bind(properties[i], CreateArgument(parameterInfo, factoryContext)));
                     bindings.Add(Expression.Bind(properties[i], CreateArgument(parameterInfo, factoryContext)));

+ 1 - 558
src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs

@@ -21,6 +21,7 @@ using System.Text.Json.Nodes;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization.Metadata;
 using System.Text.Json.Serialization.Metadata;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http.Generators.Tests;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Features;
 using Microsoft.AspNetCore.Http.Features;
 using Microsoft.AspNetCore.Http.HttpResults;
 using Microsoft.AspNetCore.Http.HttpResults;
@@ -212,30 +213,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Equal("methodInfo", exNullMethodInfo1.ParamName);
         Assert.Equal("methodInfo", exNullMethodInfo1.ParamName);
     }
     }
 
 
-    private record ParameterListFromRoute(HttpContext HttpContext, int Value);
-
-    [Fact]
-    public async Task RequestDelegatePopulatesFromRouteParameterBased_FromParameterList()
-    {
-        const string paramName = "value";
-        const int originalRouteParam = 42;
-
-        static void TestAction([AsParameters] ParameterListFromRoute args)
-        {
-            args.HttpContext.Items.Add("input", args.Value);
-        }
-
-        var httpContext = CreateHttpContext();
-        httpContext.Request.RouteValues[paramName] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo);
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(originalRouteParam, httpContext.Items["input"]);
-    }
-
     private static void TestOptional(HttpContext httpContext, [FromRoute] int value = 42)
     private static void TestOptional(HttpContext httpContext, [FromRoute] int value = 42)
     {
     {
         httpContext.Items.Add("input", value);
         httpContext.Items.Add("input", value);
@@ -1037,66 +1014,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Equal("Write more tests!", todo1!.Name);
         Assert.Equal("Write more tests!", todo1!.Name);
     }
     }
 
 
-    private record ParameterListFromQuery([FromQuery] int Value);
-
-    [Fact]
-    public async Task RequestDelegatePopulatesFromQueryParameter_FromParameterList()
-    {
-        // QueryCollection is case sensitve, since we now getting
-        // the parameter name from the Property/Record constructor
-        // we should match the case here
-        const string paramName = "Value";
-        const int originalQueryParam = 42;
-
-        int? deserializedRouteParam = null;
-
-        void TestAction([AsParameters] ParameterListFromQuery args)
-        {
-            deserializedRouteParam = args.Value;
-        }
-
-        var query = new QueryCollection(new Dictionary<string, StringValues>()
-        {
-            [paramName] = originalQueryParam.ToString(NumberFormatInfo.InvariantInfo)
-        });
-
-        var httpContext = CreateHttpContext();
-        httpContext.Request.Query = query;
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(originalQueryParam, deserializedRouteParam);
-    }
-
-    private record ParameterListFromHeader([FromHeader(Name = "X-Custom-Header")] int Value);
-
-    [Fact]
-    public async Task RequestDelegatePopulatesFromHeaderParameter_FromParameterList()
-    {
-        const string customHeaderName = "X-Custom-Header";
-        const int originalHeaderParam = 42;
-
-        int? deserializedRouteParam = null;
-
-        void TestAction([AsParameters] ParameterListFromHeader args)
-        {
-            deserializedRouteParam = args.Value;
-        }
-
-        var httpContext = CreateHttpContext();
-        httpContext.Request.Headers[customHeaderName] = originalHeaderParam.ToString(NumberFormatInfo.InvariantInfo);
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(originalHeaderParam, deserializedRouteParam);
-    }
-
     private record ParametersListWithImplictFromBody(HttpContext HttpContext, TodoStruct Todo);
     private record ParametersListWithImplictFromBody(HttpContext HttpContext, TodoStruct Todo);
 
 
     private record ParametersListWithExplictFromBody(HttpContext HttpContext, [FromBody] Todo Todo);
     private record ParametersListWithExplictFromBody(HttpContext HttpContext, [FromBody] Todo Todo);
@@ -1366,65 +1283,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Equal(errorMessage, exception.Message);
         Assert.Equal(errorMessage, exception.Message);
     }
     }
 
 
-    private record BadArgumentListRecord(int Foo)
-    {
-        public BadArgumentListRecord(int foo, int bar)
-            : this(foo)
-        {
-        }
-
-        public int Bar { get; set; }
-    }
-
-    private class BadNoPublicConstructorArgumentListClass
-    {
-        private BadNoPublicConstructorArgumentListClass()
-        { }
-
-        public int Foo { get; set; }
-    }
-
-    private abstract class BadAbstractArgumentListClass
-    {
-        public int Foo { get; set; }
-    }
-
-    private class BadArgumentListClass
-    {
-        public BadArgumentListClass(int foo, string name)
-        {
-        }
-
-        public int Foo { get; set; }
-        public int Bar { get; set; }
-    }
-
-    private class BadArgumentListClassMultipleCtors
-    {
-        public BadArgumentListClassMultipleCtors(int foo)
-        {
-        }
-
-        public BadArgumentListClassMultipleCtors(int foo, int bar)
-        {
-        }
-
-        public int Foo { get; set; }
-        public int Bar { get; set; }
-    }
-
-    private record NestedArgumentListRecord([AsParameters] object NestedParameterList);
-
-    private class ClassWithParametersConstructor
-    {
-        public ClassWithParametersConstructor([AsParameters] object nestedParameterList)
-        {
-            NestedParameterList = nestedParameterList;
-        }
-
-        public object NestedParameterList { get; set; }
-    }
-
     [Fact]
     [Fact]
     public void BuildRequestDelegateThrowsNotSupportedExceptionForNestedParametersList()
     public void BuildRequestDelegateThrowsNotSupportedExceptionForNestedParametersList()
     {
     {
@@ -1612,32 +1470,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Same(httpContext, httpContextArgument);
         Assert.Same(httpContext, httpContextArgument);
     }
     }
 
 
-    private record ParametersListWithHttpContext(
-        HttpContext HttpContext,
-        ClaimsPrincipal User,
-        HttpRequest HttpRequest,
-        HttpResponse HttpResponse);
-
-    [Fact]
-    public async Task RequestDelegatePopulatesHttpContextParameterWithoutAttribute_FromParameterList()
-    {
-        HttpContext? httpContextArgument = null;
-
-        void TestAction([AsParameters] ParametersListWithHttpContext args)
-        {
-            httpContextArgument = args.HttpContext;
-        }
-
-        var httpContext = CreateHttpContext();
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Same(httpContext, httpContextArgument);
-    }
-
     [Fact]
     [Fact]
     public async Task RequestDelegatePassHttpContextRequestAbortedAsCancellationToken()
     public async Task RequestDelegatePassHttpContextRequestAbortedAsCancellationToken()
     {
     {
@@ -1683,27 +1515,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Equal(httpContext.User, userArgument);
         Assert.Equal(httpContext.User, userArgument);
     }
     }
 
 
-    [Fact]
-    public async Task RequestDelegatePassHttpContextUserAsClaimsPrincipal_FromParameterList()
-    {
-        ClaimsPrincipal? userArgument = null;
-
-        void TestAction([AsParameters] ParametersListWithHttpContext args)
-        {
-            userArgument = args.User;
-        }
-
-        var httpContext = CreateHttpContext();
-        httpContext.User = new ClaimsPrincipal();
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(httpContext.User, userArgument);
-    }
-
     [Fact]
     [Fact]
     public async Task RequestDelegatePassHttpContextRequestAsHttpRequest()
     public async Task RequestDelegatePassHttpContextRequestAsHttpRequest()
     {
     {
@@ -1724,26 +1535,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Equal(httpContext.Request, httpRequestArgument);
         Assert.Equal(httpContext.Request, httpRequestArgument);
     }
     }
 
 
-    [Fact]
-    public async Task RequestDelegatePassHttpContextRequestAsHttpRequest_FromParameterList()
-    {
-        HttpRequest? httpRequestArgument = null;
-
-        void TestAction([AsParameters] ParametersListWithHttpContext args)
-        {
-            httpRequestArgument = args.HttpRequest;
-        }
-
-        var httpContext = CreateHttpContext();
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(httpContext.Request, httpRequestArgument);
-    }
-
     [Fact]
     [Fact]
     public async Task RequestDelegatePassesHttpContextRresponseAsHttpResponse()
     public async Task RequestDelegatePassesHttpContextRresponseAsHttpResponse()
     {
     {
@@ -1764,26 +1555,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Equal(httpContext.Response, httpResponseArgument);
         Assert.Equal(httpContext.Response, httpResponseArgument);
     }
     }
 
 
-    [Fact]
-    public async Task RequestDelegatePassesHttpContextRresponseAsHttpResponse_FromParameterList()
-    {
-        HttpResponse? httpResponseArgument = null;
-
-        void TestAction([AsParameters] ParametersListWithHttpContext args)
-        {
-            httpResponseArgument = args.HttpResponse;
-        }
-
-        var httpContext = CreateHttpContext();
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(httpContext.Response, httpResponseArgument);
-    }
-
     public static IEnumerable<object[]> ComplexResult
     public static IEnumerable<object[]> ComplexResult
     {
     {
         get
         get
@@ -2921,176 +2692,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Equal("Assigning a value to the IFromFormMetadata.Name property is not supported for parameters of type IFormCollection.", nse.Message);
         Assert.Equal("Assigning a value to the IFromFormMetadata.Name property is not supported for parameters of type IFormCollection.", nse.Message);
     }
     }
 
 
-    private record struct ParameterListRecordStruct(HttpContext HttpContext, [FromRoute] int Value);
-
-    private record ParameterListRecordClass(HttpContext HttpContext, [FromRoute] int Value);
-
-    private record ParameterListRecordWithoutPositionalParameters
-    {
-        public HttpContext? HttpContext { get; set; }
-
-        [FromRoute]
-        public int Value { get; set; }
-    }
-
-    private struct ParameterListStruct
-    {
-        public HttpContext HttpContext { get; set; }
-
-        [FromRoute]
-        public int Value { get; set; }
-    }
-
-    private struct ParameterListMutableStruct
-    {
-        public ParameterListMutableStruct()
-        {
-            Value = -1;
-            HttpContext = default!;
-        }
-
-        public HttpContext HttpContext { get; set; }
-
-        [FromRoute]
-        public int Value { get; set; }
-    }
-
-    private class ParameterListStructWithParameterizedContructor
-    {
-        public ParameterListStructWithParameterizedContructor(HttpContext httpContext)
-        {
-            HttpContext = httpContext;
-            Value = 42;
-        }
-
-        public HttpContext HttpContext { get; set; }
-
-        public int Value { get; set; }
-    }
-
-    private struct ParameterListStructWithMultipleParameterizedContructor
-    {
-        public ParameterListStructWithMultipleParameterizedContructor(HttpContext httpContext)
-        {
-            HttpContext = httpContext;
-            Value = 10;
-        }
-
-        public ParameterListStructWithMultipleParameterizedContructor(HttpContext httpContext, [FromHeader(Name = "Value")] int value)
-        {
-            HttpContext = httpContext;
-            Value = value;
-        }
-
-        public HttpContext HttpContext { get; set; }
-
-        [FromRoute]
-        public int Value { get; set; }
-    }
-
-    private class ParameterListClass
-    {
-        public HttpContext? HttpContext { get; set; }
-
-        [FromRoute]
-        public int Value { get; set; }
-    }
-
-    private class ParameterListClassWithParameterizedContructor
-    {
-        public ParameterListClassWithParameterizedContructor(HttpContext httpContext)
-        {
-            HttpContext = httpContext;
-            Value = 42;
-        }
-
-        public HttpContext HttpContext { get; set; }
-
-        public int Value { get; set; }
-    }
-
-    public static object[][] FromParameterListActions
-    {
-        get
-        {
-            void TestParameterListRecordStruct([AsParameters] ParameterListRecordStruct args)
-            {
-                args.HttpContext.Items.Add("input", args.Value);
-            }
-
-            void TestParameterListRecordClass([AsParameters] ParameterListRecordClass args)
-            {
-                args.HttpContext.Items.Add("input", args.Value);
-            }
-
-            void TestParameterListRecordWithoutPositionalParameters([AsParameters] ParameterListRecordWithoutPositionalParameters args)
-            {
-                args.HttpContext!.Items.Add("input", args.Value);
-            }
-
-            void TestParameterListStruct([AsParameters] ParameterListStruct args)
-            {
-                args.HttpContext.Items.Add("input", args.Value);
-            }
-
-            void TestParameterListMutableStruct([AsParameters] ParameterListMutableStruct args)
-            {
-                args.HttpContext.Items.Add("input", args.Value);
-            }
-
-            void TestParameterListStructWithParameterizedContructor([AsParameters] ParameterListStructWithParameterizedContructor args)
-            {
-                args.HttpContext.Items.Add("input", args.Value);
-            }
-
-            void TestParameterListStructWithMultipleParameterizedContructor([AsParameters] ParameterListStructWithMultipleParameterizedContructor args)
-            {
-                args.HttpContext.Items.Add("input", args.Value);
-            }
-
-            void TestParameterListClass([AsParameters] ParameterListClass args)
-            {
-                args.HttpContext!.Items.Add("input", args.Value);
-            }
-
-            void TestParameterListClassWithParameterizedContructor([AsParameters] ParameterListClassWithParameterizedContructor args)
-            {
-                args.HttpContext.Items.Add("input", args.Value);
-            }
-
-            return new[]
-            {
-                new object[] { (Action<ParameterListRecordStruct>)TestParameterListRecordStruct },
-                new object[] { (Action<ParameterListRecordClass>)TestParameterListRecordClass },
-                new object[] { (Action<ParameterListRecordWithoutPositionalParameters>)TestParameterListRecordWithoutPositionalParameters },
-                new object[] { (Action<ParameterListStruct>)TestParameterListStruct },
-                new object[] { (Action<ParameterListMutableStruct>)TestParameterListMutableStruct },
-                new object[] { (Action<ParameterListStructWithParameterizedContructor>)TestParameterListStructWithParameterizedContructor },
-                new object[] { (Action<ParameterListStructWithMultipleParameterizedContructor>)TestParameterListStructWithMultipleParameterizedContructor },
-                new object[] { (Action<ParameterListClass>)TestParameterListClass },
-                new object[] { (Action<ParameterListClassWithParameterizedContructor>)TestParameterListClassWithParameterizedContructor },
-            };
-        }
-    }
-
-    [Theory]
-    [MemberData(nameof(FromParameterListActions))]
-    public async Task RequestDelegatePopulatesFromParameterList(Delegate action)
-    {
-        const string paramName = "value";
-        const int originalRouteParam = 42;
-
-        var httpContext = CreateHttpContext();
-        httpContext.Request.RouteValues[paramName] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo);
-
-        var factoryResult = RequestDelegateFactory.Create(action);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(originalRouteParam, httpContext.Items["input"]);
-    }
-
     public static object[][] NullableFromParameterListActions
     public static object[][] NullableFromParameterListActions
     {
     {
         get
         get
@@ -3128,34 +2729,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         Assert.Contains($"The nullable type '{TypeNameHelper.GetTypeDisplayName(parameter.ParameterType, fullName: false)}' is not supported, mark the parameter as non-nullable.", exception.Message);
         Assert.Contains($"The nullable type '{TypeNameHelper.GetTypeDisplayName(parameter.ParameterType, fullName: false)}' is not supported, mark the parameter as non-nullable.", exception.Message);
     }
     }
 
 
-    private record struct SampleParameterList(int Foo);
-    private record struct AdditionalSampleParameterList(int Bar);
-
-    [Fact]
-    public async Task RequestDelegatePopulatesFromMultipleParameterLists()
-    {
-        const int foo = 1;
-        const int bar = 2;
-
-        void TestAction(HttpContext context, [AsParameters] SampleParameterList args, [AsParameters] AdditionalSampleParameterList args2)
-        {
-            context.Items.Add("foo", args.Foo);
-            context.Items.Add("bar", args2.Bar);
-        }
-
-        var httpContext = CreateHttpContext();
-        httpContext.Request.RouteValues[nameof(SampleParameterList.Foo)] = foo.ToString(NumberFormatInfo.InvariantInfo);
-        httpContext.Request.RouteValues[nameof(AdditionalSampleParameterList.Bar)] = bar.ToString(NumberFormatInfo.InvariantInfo);
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(foo, httpContext.Items["foo"]);
-        Assert.Equal(bar, httpContext.Items["bar"]);
-    }
-
     [Fact]
     [Fact]
     public void RequestDelegateThrowsWhenParameterNameConflicts()
     public void RequestDelegateThrowsWhenParameterNameConflicts()
     {
     {
@@ -3183,87 +2756,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         public int ReadOnlyValue { get; }
         public int ReadOnlyValue { get; }
     }
     }
 
 
-    [Fact]
-    public async Task RequestDelegatePopulatesFromParameterListAndSkipReadOnlyProperties()
-    {
-        const int routeParamValue = 42;
-        var expectedInput = new ParameterListWithReadOnlyProperties() { Value = routeParamValue };
-
-        void TestAction(HttpContext context, [AsParameters] ParameterListWithReadOnlyProperties args)
-        {
-            context.Items.Add("input", args);
-        }
-
-        var httpContext = CreateHttpContext();
-        httpContext.Request.RouteValues[nameof(ParameterListWithReadOnlyProperties.Value)] = routeParamValue.ToString(NumberFormatInfo.InvariantInfo);
-        httpContext.Request.RouteValues[nameof(ParameterListWithReadOnlyProperties.ConstantValue)] = routeParamValue.ToString(NumberFormatInfo.InvariantInfo);
-        httpContext.Request.RouteValues[nameof(ParameterListWithReadOnlyProperties.ReadOnlyValue)] = routeParamValue.ToString(NumberFormatInfo.InvariantInfo);
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        var input = Assert.IsType<ParameterListWithReadOnlyProperties>(httpContext.Items["input"]);
-        Assert.Equal(expectedInput.Value, input.Value);
-        Assert.Equal(expectedInput.ConstantValue, input.ConstantValue);
-        Assert.Equal(expectedInput.ReadOnlyValue, input.ReadOnlyValue);
-    }
-
-    private record ParameterListRecordWitDefaultValue(HttpContext HttpContext, [FromRoute] int Value = 42);
-
-    [Fact]
-    public async Task RequestDelegatePopulatesFromParameterListRecordUsesDefaultValue()
-    {
-        const int expectedValue = 42;
-
-        void TestAction([AsParameters] ParameterListRecordWitDefaultValue args)
-        {
-            args.HttpContext.Items.Add("input", args.Value);
-        }
-
-        var httpContext = CreateHttpContext();
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(expectedValue, httpContext.Items["input"]);
-    }
-
-    private class ParameterListWitDefaultValue
-    {
-        public ParameterListWitDefaultValue(HttpContext httpContext, [FromRoute] int value = 42)
-        {
-            HttpContext = httpContext;
-            Value = value;
-        }
-
-        public HttpContext HttpContext { get; }
-        public int Value { get; }
-    }
-
-    [Fact]
-    public async Task RequestDelegatePopulatesFromParameterListUsesDefaultValue()
-    {
-        const int expectedValue = 42;
-
-        void TestAction([AsParameters] ParameterListWitDefaultValue args)
-        {
-            args.HttpContext.Items.Add("input", args.Value);
-        }
-
-        var httpContext = CreateHttpContext();
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        await requestDelegate(httpContext);
-
-        Assert.Equal(expectedValue, httpContext.Items["input"]);
-    }
-
     [Fact]
     [Fact]
     public async Task RequestDelegateFactory_InvokesFiltersButNotHandler_OnArgumentError()
     public async Task RequestDelegateFactory_InvokesFiltersButNotHandler_OnArgumentError()
     {
     {
@@ -4638,55 +4130,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         await result.RequestDelegate(httpContext);
         await result.RequestDelegate(httpContext);
     }
     }
 
 
-    private class ParameterListRequiredStringFromDifferentSources
-    {
-        public HttpContext? HttpContext { get; set; }
-
-        [FromRoute]
-        public required string RequiredRouteParam { get; set; }
-
-        [FromQuery]
-        public required string RequiredQueryParam { get; set; }
-
-        [FromHeader]
-        public required string RequiredHeaderParam { get; set; }
-    }
-
-    [Fact]
-    public async Task RequestDelegateFactory_AsParameters_SupportsRequiredMember()
-    {
-        // Arrange
-        static void TestAction([AsParameters] ParameterListRequiredStringFromDifferentSources args) { }
-
-        var httpContext = CreateHttpContext();
-
-        var factoryResult = RequestDelegateFactory.Create(TestAction);
-        var requestDelegate = factoryResult.RequestDelegate;
-
-        // Act
-        await requestDelegate(httpContext);
-
-        // Assert that the required modifier on members that
-        // are not nullable treats them as required.
-        Assert.Equal(400, httpContext.Response.StatusCode);
-
-        var logs = TestSink.Writes.ToArray();
-
-        Assert.Equal(3, logs.Length);
-
-        Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
-        Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
-        Assert.Equal(@"Required parameter ""string RequiredRouteParam"" was not provided from route.", logs[0].Message);
-
-        Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[1].EventId);
-        Assert.Equal(LogLevel.Debug, logs[1].LogLevel);
-        Assert.Equal(@"Required parameter ""string RequiredQueryParam"" was not provided from query string.", logs[1].Message);
-
-        Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[2].EventId);
-        Assert.Equal(LogLevel.Debug, logs[2].LogLevel);
-        Assert.Equal(@"Required parameter ""string RequiredHeaderParam"" was not provided from header.", logs[2].Message);
-    }
-
     private class ParameterListRequiredNullableStringFromDifferentSources
     private class ParameterListRequiredNullableStringFromDifferentSources
     {
     {
         public HttpContext? HttpContext { get; set; }
         public HttpContext? HttpContext { get; set; }

+ 56 - 20
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -357,9 +357,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MyBindAsyncRecord, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MyBindAsyncRecord, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -444,9 +446,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MyBindAsyncRecord?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MyBindAsyncRecord?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -509,9 +513,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MyBindAsyncStruct, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MyBindAsyncStruct, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -596,9 +602,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MyBindAsyncStruct?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MyBindAsyncStruct?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -661,9 +669,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MyNullableBindAsyncStruct, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MyNullableBindAsyncStruct, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -748,9 +758,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MyNullableBindAsyncStruct?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MyNullableBindAsyncStruct?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -813,9 +825,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MyBothBindAsyncStruct, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MyBothBindAsyncStruct, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -900,9 +914,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MyBothBindAsyncStruct?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MyBothBindAsyncStruct?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -965,9 +981,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MySimpleBindAsyncRecord, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MySimpleBindAsyncRecord, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -1051,9 +1069,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MySimpleBindAsyncRecord?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MySimpleBindAsyncRecord?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -1115,9 +1135,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MySimpleBindAsyncStruct, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.MySimpleBindAsyncStruct, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -1201,9 +1223,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MySimpleBindAsyncStruct?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.MySimpleBindAsyncStruct?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -1265,9 +1289,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.BindAsyncFromImplicitStaticAbstractInterface, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.BindAsyncFromImplicitStaticAbstractInterface, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -1352,9 +1378,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.BindAsyncFromImplicitStaticAbstractInterface?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.BindAsyncFromImplicitStaticAbstractInterface?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -1417,9 +1445,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.InheritBindAsync, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.InheritBindAsync, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -1503,9 +1533,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.InheritBindAsync?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.InheritBindAsync?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -1567,9 +1599,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.BindAsyncFromExplicitStaticAbstractInterface, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.Generators.Tests.BindAsyncFromExplicitStaticAbstractInterface, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -1654,9 +1688,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.BindAsyncFromExplicitStaticAbstractInterface?, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.BindAsyncFromExplicitStaticAbstractInterface?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var parameters = del.Method.GetParameters();
                     var parameters = del.Method.GetParameters();
 
 
@@ -1792,7 +1828,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 8 - 4
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -103,9 +103,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo, global::Microsoft.AspNetCore.Http.HttpResults.Ok<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo, global::Microsoft.AspNetCore.Http.HttpResults.Ok<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -177,9 +179,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo?, global::Microsoft.AspNetCore.Http.HttpResults.Ok<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo?, global::Microsoft.AspNetCore.Http.HttpResults.Ok<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -388,7 +392,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.ParsableTodo[], global::System.Int32>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.ParsableTodo[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
@@ -268,7 +270,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
@@ -240,7 +242,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
@@ -240,7 +242,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.ParsableTodo[], global::System.Int32>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.ParsableTodo[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
@@ -268,7 +270,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
 
 
@@ -239,7 +241,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
 
 
@@ -239,7 +241,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 11 - 5
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -132,9 +132,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.TestService, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.TestService, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -196,9 +198,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.Collections.Generic.IEnumerable<global::Microsoft.AspNetCore.Http.Generators.Tests.TestService>, global::System.String>)del;
                     var handler = (System.Func<global::System.Collections.Generic.IEnumerable<global::Microsoft.AspNetCore.Http.Generators.Tests.TestService>, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -260,9 +264,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.TestService?, global::System.Collections.Generic.IEnumerable<global::Microsoft.AspNetCore.Http.Generators.Tests.TestService>, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.TestService?, global::System.Collections.Generic.IEnumerable<global::Microsoft.AspNetCore.Http.Generators.Tests.TestService>, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -396,7 +402,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 19 - 9
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -180,9 +182,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -258,9 +262,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -289,7 +295,7 @@ namespace Microsoft.AspNetCore.Http.Generated
                         if (routeValue_raw == null)
                         if (routeValue_raw == null)
                         {
                         {
                             wasParamCheckFailure = true;
                             wasParamCheckFailure = true;
-                            logOrThrowExceptionHelper.RequiredParameterNotProvided("string", "routeValue", "unknown");
+                            logOrThrowExceptionHelper.RequiredParameterNotProvided("string", "routeValue", "route");
                         }
                         }
                         var routeValue_temp = (string?)routeValue_raw;
                         var routeValue_temp = (string?)routeValue_raw;
                         string routeValue_local = routeValue_temp!;
                         string routeValue_local = routeValue_temp!;
@@ -316,7 +322,7 @@ namespace Microsoft.AspNetCore.Http.Generated
                         if (routeValue_raw == null)
                         if (routeValue_raw == null)
                         {
                         {
                             wasParamCheckFailure = true;
                             wasParamCheckFailure = true;
-                            logOrThrowExceptionHelper.RequiredParameterNotProvided("string", "routeValue", "unknown");
+                            logOrThrowExceptionHelper.RequiredParameterNotProvided("string", "routeValue", "route");
                         }
                         }
                         var routeValue_temp = (string?)routeValue_raw;
                         var routeValue_temp = (string?)routeValue_raw;
                         string routeValue_local = routeValue_temp!;
                         string routeValue_local = routeValue_temp!;
@@ -344,9 +350,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("value", options?.RouteParameterNames);
                     var value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("value", options?.RouteParameterNames);
 
 
@@ -423,9 +431,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     var handler = (System.Func<global::System.String, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("value", options?.RouteParameterNames);
                     var value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("value", options?.RouteParameterNames);
 
 
@@ -576,7 +586,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.ParsableTodo[], global::System.Int32>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.ParsableTodo[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var p_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("p", options?.RouteParameterNames);
                     var p_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("p", options?.RouteParameterNames);
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
@@ -275,7 +277,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
 
 
@@ -239,7 +241,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
 
 
@@ -239,7 +241,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String?[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
 
 
@@ -239,7 +241,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
                     var jsonTypeInfo =  (JsonTypeInfo<global::System.Int32>)serializerOptions.GetTypeInfo(typeof(global::System.Int32));
 
 
@@ -239,7 +241,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -116,9 +116,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo, global::Microsoft.AspNetCore.Http.Generators.Tests.TestService, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo, global::Microsoft.AspNetCore.Http.Generators.Tests.TestService, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
                     var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
                     var todo_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>(logOrThrowExceptionHelper, "Todo", "todo", serviceProviderIsService);
                     var todo_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>(logOrThrowExceptionHelper, "Todo", "todo", serviceProviderIsService);
@@ -367,7 +369,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 4 - 2
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,6 +102,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpRequest, global::Microsoft.AspNetCore.Http.HttpResponse, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpRequest, global::Microsoft.AspNetCore.Http.HttpResponse, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -232,7 +234,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String, global::System.String, global::System.String>)del;
                     var handler = (System.Func<global::System.String, global::System.String, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -266,7 +268,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 4 - 2
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,6 +102,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String>)del;
                     var handler = (System.Func<global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -226,7 +228,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 4 - 2
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,6 +102,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String>)del;
                     var handler = (System.Func<global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -226,7 +228,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var serializerOptions = serviceProvider?.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? new JsonOptions().SerializerOptions;
                     var jsonTypeInfo =  (JsonTypeInfo<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>)serializerOptions.GetTypeInfo(typeof(global::Microsoft.AspNetCore.Http.Generators.Tests.Todo));
                     var jsonTypeInfo =  (JsonTypeInfo<global::Microsoft.AspNetCore.Http.Generators.Tests.Todo>)serializerOptions.GetTypeInfo(typeof(global::Microsoft.AspNetCore.Http.Generators.Tests.Todo));
 
 
@@ -242,7 +244,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 4 - 2
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsValidationProblemResult_Has_Metadata.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,6 +102,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpResults.ValidationProblem>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpResults.ValidationProblem>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -206,7 +208,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 4 - 2
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsVoid_Has_No_Metadata.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -101,6 +101,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Action)del;
                     var handler = (System.Action)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -201,7 +203,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.TryParseTodo, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.TryParseTodo, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -266,7 +268,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.TodoStatus, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.TodoStatus, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -266,7 +268,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String?, global::System.String>)del;
                     var handler = (System.Func<global::System.String?, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -238,7 +240,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -118,9 +118,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.CustomMetadataEmitter>)del;
                     var handler = (System.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.CustomMetadataEmitter>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
                     var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
                     var x_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::Microsoft.AspNetCore.Http.Generators.Tests.CustomMetadataEmitter>(logOrThrowExceptionHelper, "CustomMetadataEmitter", "x", serviceProviderIsService);
                     var x_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::Microsoft.AspNetCore.Http.Generators.Tests.CustomMetadataEmitter>(logOrThrowExceptionHelper, "CustomMetadataEmitter", "x", serviceProviderIsService);
@@ -347,7 +349,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -115,9 +115,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String[], global::System.String>)del;
                     var handler = (System.Func<global::System.String[], global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
                     var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
                     var p_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::System.String[]>(logOrThrowExceptionHelper, "string[]", "p", serviceProviderIsService);
                     var p_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::System.String[]>(logOrThrowExceptionHelper, "string[]", "p", serviceProviderIsService);
@@ -351,7 +353,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -115,9 +115,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String[], global::System.Int32>)del;
                     var handler = (System.Func<global::System.String[], global::System.Int32>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
                     var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
                     var p_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::System.String[]>(logOrThrowExceptionHelper, "string[]", "p", serviceProviderIsService);
                     var p_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::System.String[]>(logOrThrowExceptionHelper, "string[]", "p", serviceProviderIsService);
@@ -353,7 +355,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 10 - 2
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -132,6 +132,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String>)del;
                     var handler = (System.Func<global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -188,6 +190,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.String>)del;
                     var handler = (System.Func<global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -244,6 +248,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.Threading.Tasks.Task<global::System.String>>)del;
                     var handler = (System.Func<global::System.Threading.Tasks.Task<global::System.String>>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -301,6 +307,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::System.Threading.Tasks.ValueTask<global::System.String>>)del;
                     var handler = (System.Func<global::System.Threading.Tasks.ValueTask<global::System.String>>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -426,7 +434,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 8 - 2
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -132,6 +132,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpRequest, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpRequest, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -192,6 +194,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpResponse, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpResponse, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -252,6 +256,8 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpRequest, global::Microsoft.AspNetCore.Http.HttpResponse, global::System.String>)del;
                     var handler = (System.Func<global::Microsoft.AspNetCore.Http.HttpRequest, global::Microsoft.AspNetCore.Http.HttpResponse, global::System.String>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
 
 
@@ -382,7 +388,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 5 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/RequestDelegateValidateGeneratedFormCode.generated.txt

@@ -11,7 +11,7 @@
 namespace Microsoft.AspNetCore.Builder
 namespace Microsoft.AspNetCore.Builder
 {
 {
     %GENERATEDCODEATTRIBUTE%
     %GENERATEDCODEATTRIBUTE%
-    internal class SourceKey
+    internal sealed class SourceKey
     {
     {
         public string Path { get; init; }
         public string Path { get; init; }
         public int Line { get; init; }
         public int Line { get; init; }
@@ -102,9 +102,11 @@ namespace Microsoft.AspNetCore.Http.Generated
                 },
                 },
                 (del, options, inferredMetadataResult) =>
                 (del, options, inferredMetadataResult) =>
                 {
                 {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
                     var handler = (System.Action<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.IFormFile, global::Microsoft.AspNetCore.Http.IFormFileCollection, global::Microsoft.AspNetCore.Http.IFormCollection, global::Microsoft.AspNetCore.Http.Generators.Tests.MyTryParseRecord>)del;
                     var handler = (System.Action<global::Microsoft.AspNetCore.Http.HttpContext, global::Microsoft.AspNetCore.Http.IFormFile, global::Microsoft.AspNetCore.Http.IFormFileCollection, global::Microsoft.AspNetCore.Http.IFormCollection, global::Microsoft.AspNetCore.Http.Generators.Tests.MyTryParseRecord>)del;
                     EndpointFilterDelegate? filteredInvocation = null;
                     EndpointFilterDelegate? filteredInvocation = null;
-                    var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
                     var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
 
 
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
                     if (options?.EndpointBuilder?.FilterFactories.Count > 0)
@@ -384,7 +386,7 @@ namespace Microsoft.AspNetCore.Http.Generated
     }
     }
 
 
 
 
-    file class LogOrThrowExceptionHelper
+    file sealed class LogOrThrowExceptionHelper
     {
     {
         private readonly ILogger? _rdgLogger;
         private readonly ILogger? _rdgLogger;
         private readonly bool _shouldThrow;
         private readonly bool _shouldThrow;

+ 1042 - 0
src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt

@@ -0,0 +1,1042 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+#nullable enable
+
+namespace Microsoft.AspNetCore.Builder
+{
+    %GENERATEDCODEATTRIBUTE%
+    internal sealed 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.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue> 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);
+        }
+        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.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct> 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);
+        }
+        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.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext> 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);
+        }
+        internal static global::Microsoft.AspNetCore.Builder.RouteHandlerBuilder MapPost(
+            this global::Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints,
+            [global::System.Diagnostics.CodeAnalysis.StringSyntax("Route")] string pattern,
+            global::System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody, global::System.String> 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,
+                PostVerb,
+                filePath,
+                lineNumber);
+        }
+        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.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType> 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.Diagnostics.CodeAnalysis;
+    using System.Globalization;
+    using System.Linq;
+    using System.Reflection;
+    using System.Text.Json;
+    using System.Text.Json.Serialization.Metadata;
+    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.Json;
+    using Microsoft.AspNetCore.Http.Metadata;
+    using Microsoft.Extensions.DependencyInjection;
+    using Microsoft.Extensions.FileProviders;
+    using Microsoft.Extensions.Logging;
+    using Microsoft.Extensions.Primitives;
+    using Microsoft.Extensions.Options;
+
+    using MetadataPopulator = System.Func<System.Reflection.MethodInfo, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions?, Microsoft.AspNetCore.Http.RequestDelegateMetadataResult>;
+    using RequestDelegateFactoryFunc = System.Func<System.Delegate, Microsoft.AspNetCore.Http.RequestDelegateFactoryOptions, Microsoft.AspNetCore.Http.RequestDelegateMetadataResult?, Microsoft.AspNetCore.Http.RequestDelegateResult>;
+
+    file static class GeneratedRouteBuilderExtensionsCore
+    {
+
+        private static readonly Dictionary<(string, int), (MetadataPopulator, RequestDelegateFactoryFunc)> map = new()
+        {
+            [(@"TestMapActions.cs", 44)] = (
+                (methodInfo, options) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 44));
+                    return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() };
+                },
+                (del, options, inferredMetadataResult) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    var handler = (System.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue>)del;
+                    EndpointFilterDelegate? filteredInvocation = null;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
+                    var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
+
+                    if (options?.EndpointBuilder?.FilterFactories.Count > 0)
+                    {
+                        filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic =>
+                        {
+                            if (ic.HttpContext.Response.StatusCode == 400)
+                            {
+                                return ValueTask.FromResult<object?>(Results.Empty);
+                            }
+                            handler(ic.GetArgument<global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue>(0)!);
+                            return ValueTask.FromResult<object?>(Results.Empty);
+                        },
+                        options.EndpointBuilder,
+                        handler.Method);
+                    }
+
+                    Task RequestHandler(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        // Endpoint Parameter: Value (Type = int, IsOptional = True, IsParsable = True, IsArray = False, Source = Route)
+                        if (options?.RouteParameterNames?.Contains("Value", StringComparer.OrdinalIgnoreCase) != true)
+                        {
+                            throw new InvalidOperationException($"'Value' is not a route parameter.");
+                        }
+                        var Value_raw = (string?)httpContext.Request.RouteValues["Value"];
+                        var Value_temp = (string?)Value_raw;
+                        int Value_parsed_temp = default;
+                        if (GeneratedRouteBuilderExtensionsCore.TryParseExplicit<int>(Value_temp!, CultureInfo.InvariantCulture, out var Value_temp_parsed_non_nullable))
+                        {
+                            Value_parsed_temp = Value_temp_parsed_non_nullable;
+                        }
+                        else if (string.IsNullOrEmpty(Value_temp))
+                        {
+                            Value_parsed_temp = 42;
+                        }
+                        else
+                        {
+                            wasParamCheckFailure = true;
+                        }
+                        int Value_local = Value_parsed_temp!;
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue(HttpContext_local, Value_local);
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                            return Task.CompletedTask;
+                        }
+                        handler(args_local);
+                        return Task.CompletedTask;
+                    }
+
+                    async Task RequestHandlerFiltered(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        // Endpoint Parameter: Value (Type = int, IsOptional = True, IsParsable = True, IsArray = False, Source = Route)
+                        if (options?.RouteParameterNames?.Contains("Value", StringComparer.OrdinalIgnoreCase) != true)
+                        {
+                            throw new InvalidOperationException($"'Value' is not a route parameter.");
+                        }
+                        var Value_raw = (string?)httpContext.Request.RouteValues["Value"];
+                        var Value_temp = (string?)Value_raw;
+                        int Value_parsed_temp = default;
+                        if (GeneratedRouteBuilderExtensionsCore.TryParseExplicit<int>(Value_temp!, CultureInfo.InvariantCulture, out var Value_temp_parsed_non_nullable))
+                        {
+                            Value_parsed_temp = Value_temp_parsed_non_nullable;
+                        }
+                        else if (string.IsNullOrEmpty(Value_temp))
+                        {
+                            Value_parsed_temp = 42;
+                        }
+                        else
+                        {
+                            wasParamCheckFailure = true;
+                        }
+                        int Value_local = Value_parsed_temp!;
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue(HttpContext_local, Value_local);
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                        }
+                        var result = await filteredInvocation(EndpointFilterInvocationContext.Create<global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListWitDefaultValue>(httpContext, args_local));
+                        await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext);
+                    }
+
+                    RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered;
+                    var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection<object>.Empty;
+                    return new RequestDelegateResult(targetDelegate, metadata);
+                }),
+            [(@"TestMapActions.cs", 45)] = (
+                (methodInfo, options) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 45));
+                    return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() };
+                },
+                (del, options, inferredMetadataResult) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    var handler = (System.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct>)del;
+                    EndpointFilterDelegate? filteredInvocation = null;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
+                    var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
+                    var Value_RouteOrQueryResolver = GeneratedRouteBuilderExtensionsCore.ResolveFromRouteOrQuery("Value", options?.RouteParameterNames);
+
+                    if (options?.EndpointBuilder?.FilterFactories.Count > 0)
+                    {
+                        filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic =>
+                        {
+                            if (ic.HttpContext.Response.StatusCode == 400)
+                            {
+                                return ValueTask.FromResult<object?>(Results.Empty);
+                            }
+                            handler(ic.GetArgument<global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct>(0)!);
+                            return ValueTask.FromResult<object?>(Results.Empty);
+                        },
+                        options.EndpointBuilder,
+                        handler.Method);
+                    }
+
+                    Task RequestHandler(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        // Endpoint Parameter: Value (Type = int, IsOptional = False, IsParsable = True, IsArray = False, Source = RouteOrQuery)
+                        var Value_raw = Value_RouteOrQueryResolver(httpContext);
+                        if (Value_raw is StringValues { Count: 0 })
+                        {
+                            wasParamCheckFailure = true;
+                            logOrThrowExceptionHelper.RequiredParameterNotProvided("int", "Value", "route or query string");
+                        }
+                        var Value_temp = (string?)Value_raw;
+                        if (!GeneratedRouteBuilderExtensionsCore.TryParseExplicit<int>(Value_temp!, CultureInfo.InvariantCulture, out var Value_parsed_temp))
+                        {
+                            if (!string.IsNullOrEmpty(Value_temp))
+                            {
+                                logOrThrowExceptionHelper.ParameterBindingFailed("int", "Value", Value_temp);
+                                wasParamCheckFailure = true;
+                            }
+                        }
+                        int Value_local = Value_parsed_temp!;
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct { HttpContext = HttpContext_local, Value = Value_local };
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                            return Task.CompletedTask;
+                        }
+                        handler(args_local);
+                        return Task.CompletedTask;
+                    }
+
+                    async Task RequestHandlerFiltered(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        // Endpoint Parameter: Value (Type = int, IsOptional = False, IsParsable = True, IsArray = False, Source = RouteOrQuery)
+                        var Value_raw = Value_RouteOrQueryResolver(httpContext);
+                        if (Value_raw is StringValues { Count: 0 })
+                        {
+                            wasParamCheckFailure = true;
+                            logOrThrowExceptionHelper.RequiredParameterNotProvided("int", "Value", "route or query string");
+                        }
+                        var Value_temp = (string?)Value_raw;
+                        if (!GeneratedRouteBuilderExtensionsCore.TryParseExplicit<int>(Value_temp!, CultureInfo.InvariantCulture, out var Value_parsed_temp))
+                        {
+                            if (!string.IsNullOrEmpty(Value_temp))
+                            {
+                                logOrThrowExceptionHelper.ParameterBindingFailed("int", "Value", Value_temp);
+                                wasParamCheckFailure = true;
+                            }
+                        }
+                        int Value_local = Value_parsed_temp!;
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct { HttpContext = HttpContext_local, Value = Value_local };
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                        }
+                        var result = await filteredInvocation(EndpointFilterInvocationContext.Create<global::Microsoft.AspNetCore.Http.Generators.Tests.ParameterListRecordStruct>(httpContext, args_local));
+                        await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext);
+                    }
+
+                    RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered;
+                    var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection<object>.Empty;
+                    return new RequestDelegateResult(targetDelegate, metadata);
+                }),
+            [(@"TestMapActions.cs", 46)] = (
+                (methodInfo, options) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 46));
+                    return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() };
+                },
+                (del, options, inferredMetadataResult) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    var handler = (System.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext>)del;
+                    EndpointFilterDelegate? filteredInvocation = null;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
+                    var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
+
+                    if (options?.EndpointBuilder?.FilterFactories.Count > 0)
+                    {
+                        filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic =>
+                        {
+                            if (ic.HttpContext.Response.StatusCode == 400)
+                            {
+                                return ValueTask.FromResult<object?>(Results.Empty);
+                            }
+                            handler(ic.GetArgument<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext>(0)!);
+                            return ValueTask.FromResult<object?>(Results.Empty);
+                        },
+                        options.EndpointBuilder,
+                        handler.Method);
+                    }
+
+                    Task RequestHandler(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        var User_local = httpContext.User;
+                        var Request_local = httpContext.Request;
+                        var Response_local = httpContext.Response;
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext(HttpContext_local, User_local, Request_local, Response_local);
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                            return Task.CompletedTask;
+                        }
+                        handler(args_local);
+                        return Task.CompletedTask;
+                    }
+
+                    async Task RequestHandlerFiltered(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        var User_local = httpContext.User;
+                        var Request_local = httpContext.Request;
+                        var Response_local = httpContext.Response;
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext(HttpContext_local, User_local, Request_local, Response_local);
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                        }
+                        var result = await filteredInvocation(EndpointFilterInvocationContext.Create<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithHttpContext>(httpContext, args_local));
+                        await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext);
+                    }
+
+                    RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered;
+                    var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection<object>.Empty;
+                    return new RequestDelegateResult(targetDelegate, metadata);
+                }),
+            [(@"TestMapActions.cs", 47)] = (
+                (methodInfo, options) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 47));
+                    options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType));
+                    options.EndpointBuilder.Metadata.Add(new GeneratedProducesResponseTypeMetadata(type: null, statusCode: StatusCodes.Status200OK, contentTypes: GeneratedMetadataConstants.PlaintextContentType));
+                    return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() };
+                },
+                (del, options, inferredMetadataResult) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    var handler = (System.Func<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody, global::System.String>)del;
+                    EndpointFilterDelegate? filteredInvocation = null;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
+                    var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
+                    var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
+                    var Todo_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct>(logOrThrowExceptionHelper, "TodoStruct", "Todo", serviceProviderIsService);
+
+                    if (options?.EndpointBuilder?.FilterFactories.Count > 0)
+                    {
+                        filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic =>
+                        {
+                            if (ic.HttpContext.Response.StatusCode == 400)
+                            {
+                                return ValueTask.FromResult<object?>(Results.Empty);
+                            }
+                            return ValueTask.FromResult<object?>(handler(ic.GetArgument<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody>(0)!));
+                        },
+                        options.EndpointBuilder,
+                        handler.Method);
+                    }
+
+                    async Task RequestHandler(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        // Endpoint Parameter: Todo (Type = Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct, IsOptional = False, IsParsable = False, IsArray = False, Source = JsonBodyOrService)
+                        var Todo_resolveJsonBodyOrServiceResult = await Todo_JsonBodyOrServiceResolver(httpContext, false);
+                        var Todo_local = Todo_resolveJsonBodyOrServiceResult.Item2;
+                        if (!Todo_resolveJsonBodyOrServiceResult.Item1)
+                        {
+                            return;
+                        }
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody(HttpContext_local, Todo_local);
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                            return;
+                        }
+                        httpContext.Response.ContentType ??= "text/plain";
+                        var result = handler(args_local);
+                        await httpContext.Response.WriteAsync(result);
+                    }
+
+                    async Task RequestHandlerFiltered(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        // Endpoint Parameter: Todo (Type = Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct, IsOptional = False, IsParsable = False, IsArray = False, Source = JsonBodyOrService)
+                        var Todo_resolveJsonBodyOrServiceResult = await Todo_JsonBodyOrServiceResolver(httpContext, false);
+                        var Todo_local = Todo_resolveJsonBodyOrServiceResult.Item2;
+                        if (!Todo_resolveJsonBodyOrServiceResult.Item1)
+                        {
+                            return;
+                        }
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody(HttpContext_local, Todo_local);
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                        }
+                        var result = await filteredInvocation(EndpointFilterInvocationContext.Create<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody>(httpContext, args_local));
+                        await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext);
+                    }
+
+                    RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered;
+                    var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection<object>.Empty;
+                    return new RequestDelegateResult(targetDelegate, metadata);
+                }),
+            [(@"TestMapActions.cs", 48)] = (
+                (methodInfo, options) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    options.EndpointBuilder.Metadata.Add(new SourceKey(@"TestMapActions.cs", 48));
+                    options.EndpointBuilder.Metadata.Add(new GeneratedAcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType));
+                    var parameterInfos = methodInfo.GetParameters();
+                    var Value_ParameterInfo = new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("Value")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[1]);
+                    PopulateMetadataForParameter<global::Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty>(Value_ParameterInfo, options.EndpointBuilder);
+                    PopulateMetadataForEndpoint<global::Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty>(methodInfo, options.EndpointBuilder);
+                    return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() };
+                },
+                (del, options, inferredMetadataResult) =>
+                {
+                    Debug.Assert(options != null, "RequestDelegateFactoryOptions not found.");
+                    Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found.");
+                    var handler = (System.Action<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType>)del;
+                    EndpointFilterDelegate? filteredInvocation = null;
+                    var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;
+                    var logOrThrowExceptionHelper = new LogOrThrowExceptionHelper(serviceProvider, options);
+                    var serviceProviderIsService = serviceProvider?.GetService<IServiceProviderIsService>();
+                    var Value_JsonBodyOrServiceResolver = ResolveJsonBodyOrService<global::Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty>(logOrThrowExceptionHelper, "AddsCustomParameterMetadataAsProperty", "Value", serviceProviderIsService);
+
+                    if (options?.EndpointBuilder?.FilterFactories.Count > 0)
+                    {
+                        filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic =>
+                        {
+                            if (ic.HttpContext.Response.StatusCode == 400)
+                            {
+                                return ValueTask.FromResult<object?>(Results.Empty);
+                            }
+                            handler(ic.GetArgument<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType>(0)!);
+                            return ValueTask.FromResult<object?>(Results.Empty);
+                        },
+                        options.EndpointBuilder,
+                        handler.Method);
+                    }
+
+                    async Task RequestHandler(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        // Endpoint Parameter: Value (Type = Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty, IsOptional = False, IsParsable = False, IsArray = False, Source = JsonBodyOrService)
+                        var Value_resolveJsonBodyOrServiceResult = await Value_JsonBodyOrServiceResolver(httpContext, false);
+                        var Value_local = Value_resolveJsonBodyOrServiceResult.Item2;
+                        if (!Value_resolveJsonBodyOrServiceResult.Item1)
+                        {
+                            return;
+                        }
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType(HttpContext_local, Value_local);
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                            return;
+                        }
+                        handler(args_local);
+                    }
+
+                    async Task RequestHandlerFiltered(HttpContext httpContext)
+                    {
+                        var wasParamCheckFailure = false;
+                        // Endpoint Parameter: args (Type = Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType, IsOptional = False, IsParsable = False, IsArray = False, Source = AsParameters)
+                        var HttpContext_local = httpContext;
+                        // Endpoint Parameter: Value (Type = Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty, IsOptional = False, IsParsable = False, IsArray = False, Source = JsonBodyOrService)
+                        var Value_resolveJsonBodyOrServiceResult = await Value_JsonBodyOrServiceResolver(httpContext, false);
+                        var Value_local = Value_resolveJsonBodyOrServiceResult.Item2;
+                        if (!Value_resolveJsonBodyOrServiceResult.Item1)
+                        {
+                            return;
+                        }
+
+                        var args_local = new global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType(HttpContext_local, Value_local);
+
+                        if (wasParamCheckFailure)
+                        {
+                            httpContext.Response.StatusCode = 400;
+                        }
+                        var result = await filteredInvocation(EndpointFilterInvocationContext.Create<global::Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType>(httpContext, args_local));
+                        await GeneratedRouteBuilderExtensionsCore.ExecuteObjectResult(result, httpContext);
+                    }
+
+                    RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered;
+                    var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection<object>.Empty;
+                    return new RequestDelegateResult(targetDelegate, metadata);
+                }),
+
+        };
+
+        internal static RouteHandlerBuilder MapCore(
+            this IEndpointRouteBuilder routes,
+            string pattern,
+            Delegate handler,
+            IEnumerable<string>? 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;
+        }
+
+        [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+            Justification = "The 'JsonSerializer.IsReflectionEnabledByDefault' feature switch, which is set to false by default for trimmed ASP.NET apps, ensures the JsonSerializer doesn't use Reflection.")]
+        [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "See above.")]
+        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 Func<HttpContext, StringValues> ResolveFromRouteOrQuery(string parameterName, IEnumerable<string>? routeParameterNames)
+        {
+            return routeParameterNames?.Contains(parameterName, StringComparer.OrdinalIgnoreCase) == true
+                ? (httpContext) => new StringValues((string?)httpContext.Request.RouteValues[parameterName])
+                : (httpContext) => httpContext.Request.Query[parameterName];
+        }
+        private static async ValueTask<(bool, T?)> TryResolveBodyAsync<T>(HttpContext httpContext, LogOrThrowExceptionHelper logOrThrowExceptionHelper, bool allowEmpty, string parameterTypeName, string parameterName, bool isInferred = false)
+        {
+            var feature = httpContext.Features.Get<Microsoft.AspNetCore.Http.Features.IHttpRequestBodyDetectionFeature>();
+
+            if (feature?.CanHaveBody == true)
+            {
+                if (!httpContext.Request.HasJsonContentType())
+                {
+                    logOrThrowExceptionHelper.UnexpectedJsonContentType(httpContext.Request.ContentType);
+                    httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
+                    return (false, default);
+                }
+                try
+                {
+                    var bodyValue = await httpContext.Request.ReadFromJsonAsync<T>();
+                    if (!allowEmpty && bodyValue == null)
+                    {
+                        if (!isInferred)
+                        {
+                            logOrThrowExceptionHelper.RequiredParameterNotProvided(parameterTypeName, parameterName, "body");
+                        }
+                        else
+                        {
+                            logOrThrowExceptionHelper.ImplicitBodyNotProvided(parameterName);
+                        }
+                        httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
+                        return (false, bodyValue);
+                    }
+                    return (true, bodyValue);
+                }
+                catch (BadHttpRequestException badHttpRequestException)
+                {
+                    logOrThrowExceptionHelper.RequestBodyIOException(badHttpRequestException);
+                    httpContext.Response.StatusCode = badHttpRequestException.StatusCode;
+                    return (false, default);
+                }
+                catch (IOException ioException)
+                {
+                    logOrThrowExceptionHelper.RequestBodyIOException(ioException);
+                    httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
+                    return (false, default);
+                }
+                catch (System.Text.Json.JsonException jsonException)
+                {
+                    logOrThrowExceptionHelper.InvalidJsonRequestBody(parameterTypeName, parameterName, jsonException);
+                    httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
+                    return (false, default);
+                }
+            }
+            else if (!allowEmpty)
+            {
+                httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
+            }
+
+            return (allowEmpty, default);
+        }
+        private static Func<HttpContext, bool, ValueTask<(bool, T?)>> ResolveJsonBodyOrService<T>(LogOrThrowExceptionHelper logOrThrowExceptionHelper, string parameterTypeName, string parameterName, IServiceProviderIsService? serviceProviderIsService = null)
+        {
+            if (serviceProviderIsService is not null)
+            {
+                if (serviceProviderIsService.IsService(typeof(T)))
+                {
+                    return static (httpContext, isOptional) => new ValueTask<(bool, T?)>((true, httpContext.RequestServices.GetService<T>()));
+                }
+            }
+            return (httpContext, isOptional) => TryResolveBodyAsync<T>(httpContext, logOrThrowExceptionHelper, isOptional, parameterTypeName, parameterName, isInferred: true);
+        }
+        private static bool TryParseExplicit<T>(string? s, IFormatProvider? provider, [MaybeNullWhen(returnValue: false)] out T result) where T: IParsable<T>
+            => T.TryParse(s, provider, out result);
+        private static void PopulateMetadataForEndpoint<T>(MethodInfo method, EndpointBuilder builder)
+            where T : IEndpointMetadataProvider
+        {
+            T.PopulateMetadata(method, builder);
+        }
+        private static void PopulateMetadataForParameter<T>(ParameterInfo parameter, EndpointBuilder builder)
+            where T : IEndpointParameterMetadataProvider
+        {
+            T.PopulateMetadata(parameter, builder);
+        }
+
+    }
+
+    file sealed class GeneratedAcceptsMetadata : IAcceptsMetadata
+    {
+        public GeneratedAcceptsMetadata(string[] contentTypes)
+        {
+            ArgumentNullException.ThrowIfNull(contentTypes);
+
+            ContentTypes = contentTypes;
+        }
+
+        public GeneratedAcceptsMetadata(Type? type, bool isOptional, string[] contentTypes)
+        {
+            ArgumentNullException.ThrowIfNull(type);
+            ArgumentNullException.ThrowIfNull(contentTypes);
+
+            RequestType = type;
+            ContentTypes = contentTypes;
+            IsOptional = isOptional;
+        }
+
+        public IReadOnlyList<string> ContentTypes { get; }
+
+        public Type? RequestType { get; }
+
+        public bool IsOptional { get; }
+    }
+    file sealed class GeneratedProducesResponseTypeMetadata : IProducesResponseTypeMetadata
+    {
+        public GeneratedProducesResponseTypeMetadata(Type? type, int statusCode, string[] contentTypes)
+        {
+            Type = type;
+            StatusCode = statusCode;
+            ContentTypes = contentTypes;
+        }
+
+        public Type? Type { get; }
+
+        public int StatusCode { get; }
+
+        public IEnumerable<string> ContentTypes { get; }
+    }
+
+    file static class GeneratedMetadataConstants
+    {
+        public static readonly string[] JsonContentType = new [] { "application/json" };
+        public static readonly string[] PlaintextContentType = new [] { "text/plain" };
+        public static readonly string[] FormFileContentType = new[] { "multipart/form-data" };
+        public static readonly string[] FormContentType = new[] { "multipart/form-data", "application/x-www-form-urlencoded" };
+    }
+
+    file sealed class PropertyAsParameterInfo : ParameterInfo
+    {
+        private readonly PropertyInfo _underlyingProperty;
+        private readonly ParameterInfo? _constructionParameterInfo;
+
+        public PropertyAsParameterInfo(bool isOptional, PropertyInfo propertyInfo)
+        {
+            Debug.Assert(propertyInfo != null, "PropertyInfo must be provided.");
+
+            AttrsImpl = (ParameterAttributes)propertyInfo.Attributes;
+            NameImpl = propertyInfo.Name;
+            MemberImpl = propertyInfo;
+            ClassImpl = propertyInfo.PropertyType;
+
+            // It is not a real parameter in the delegate, so,
+            // not defining a real position.
+            PositionImpl = -1;
+
+            _underlyingProperty = propertyInfo;
+            IsOptional = isOptional;
+        }
+
+        public PropertyAsParameterInfo(bool isOptional, PropertyInfo property, ParameterInfo? parameterInfo)
+            : this(isOptional, property)
+        {
+            _constructionParameterInfo = parameterInfo;
+        }
+
+        public override bool HasDefaultValue
+            => _constructionParameterInfo is not null && _constructionParameterInfo.HasDefaultValue;
+        public override object? DefaultValue
+            => _constructionParameterInfo?.DefaultValue;
+        public override int MetadataToken => _underlyingProperty.MetadataToken;
+        public override object? RawDefaultValue
+            => _constructionParameterInfo?.RawDefaultValue;
+
+        public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+        {
+            var attributes = _constructionParameterInfo?.GetCustomAttributes(attributeType, inherit);
+
+            if (attributes == null || attributes is { Length: 0 })
+            {
+                attributes = _underlyingProperty.GetCustomAttributes(attributeType, inherit);
+            }
+
+            return attributes;
+        }
+
+        public override object[] GetCustomAttributes(bool inherit)
+        {
+            var constructorAttributes = _constructionParameterInfo?.GetCustomAttributes(inherit);
+
+            if (constructorAttributes == null || constructorAttributes is { Length: 0 })
+            {
+                return _underlyingProperty.GetCustomAttributes(inherit);
+            }
+
+            var propertyAttributes = _underlyingProperty.GetCustomAttributes(inherit);
+
+            // Since the constructors attributes should take priority we will add them first,
+            // as we usually call it as First() or FirstOrDefault() in the argument creation
+            var mergedAttributes = new object[constructorAttributes.Length + propertyAttributes.Length];
+            Array.Copy(constructorAttributes, mergedAttributes, constructorAttributes.Length);
+            Array.Copy(propertyAttributes, 0, mergedAttributes, constructorAttributes.Length, propertyAttributes.Length);
+
+            return mergedAttributes;
+        }
+
+        public override IList<CustomAttributeData> GetCustomAttributesData()
+        {
+            var attributes = new List<CustomAttributeData>(
+                _constructionParameterInfo?.GetCustomAttributesData() ?? Array.Empty<CustomAttributeData>());
+            attributes.AddRange(_underlyingProperty.GetCustomAttributesData());
+
+            return attributes.AsReadOnly();
+        }
+
+        public override Type[] GetOptionalCustomModifiers()
+            => _underlyingProperty.GetOptionalCustomModifiers();
+
+        public override Type[] GetRequiredCustomModifiers()
+            => _underlyingProperty.GetRequiredCustomModifiers();
+
+        public override bool IsDefined(Type attributeType, bool inherit)
+        {
+            return (_constructionParameterInfo is not null && _constructionParameterInfo.IsDefined(attributeType, inherit)) ||
+                _underlyingProperty.IsDefined(attributeType, inherit);
+        }
+
+        public new bool IsOptional { get; }
+    }
+
+    file sealed class LogOrThrowExceptionHelper
+    {
+        private readonly ILogger? _rdgLogger;
+        private readonly bool _shouldThrow;
+
+        public LogOrThrowExceptionHelper(IServiceProvider? serviceProvider, RequestDelegateFactoryOptions? options)
+        {
+            var loggerFactory = serviceProvider?.GetRequiredService<ILoggerFactory>();
+            _rdgLogger = loggerFactory?.CreateLogger("Microsoft.AspNetCore.Http.RequestDelegateGenerator.RequestDelegateGenerator");
+            _shouldThrow = options?.ThrowOnBadRequest ?? false;
+        }
+
+        public void RequestBodyIOException(IOException exception)
+        {
+            if (_rdgLogger != null)
+            {
+                _requestBodyIOException(_rdgLogger, exception);
+            }
+        }
+
+        private static readonly Action<ILogger, Exception?> _requestBodyIOException =
+            LoggerMessage.Define(LogLevel.Debug, new EventId(1, "RequestBodyIOException"), "Reading the request body failed with an IOException.");
+
+        public void InvalidJsonRequestBody(string parameterTypeName, string parameterName, Exception exception)
+        {
+            if (_shouldThrow)
+            {
+                var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as JSON.", parameterTypeName, parameterName);
+                throw new BadHttpRequestException(message, exception);
+            }
+
+            if (_rdgLogger != null)
+            {
+                _invalidJsonRequestBody(_rdgLogger, parameterTypeName, parameterName, exception);
+            }
+        }
+
+        private static readonly Action<ILogger, string, string, Exception?> _invalidJsonRequestBody =
+            LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(2, "InvalidJsonRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as JSON.");
+
+        public void ParameterBindingFailed(string parameterTypeName, string parameterName, string sourceValue)
+        {
+            if (_shouldThrow)
+            {
+                var message = string.Format(CultureInfo.InvariantCulture, "Failed to bind parameter \"{0} {1}\" from \"{2}\".", parameterTypeName, parameterName, sourceValue);
+                throw new BadHttpRequestException(message);
+            }
+
+            if (_rdgLogger != null)
+            {
+                _parameterBindingFailed(_rdgLogger, parameterTypeName, parameterName, sourceValue, null);
+            }
+        }
+
+        private static readonly Action<ILogger, string, string, string, Exception?> _parameterBindingFailed =
+            LoggerMessage.Define<string, string, string>(LogLevel.Debug, new EventId(3, "ParameterBindingFailed"), "Failed to bind parameter \"{ParameterType} {ParameterName}\" from \"{SourceValue}\".");
+
+        public void RequiredParameterNotProvided(string parameterTypeName, string parameterName, string source)
+        {
+            if (_shouldThrow)
+            {
+                var message = string.Format(CultureInfo.InvariantCulture, "Required parameter \"{0} {1}\" was not provided from {2}.", parameterTypeName, parameterName, source);
+                throw new BadHttpRequestException(message);
+            }
+
+            if (_rdgLogger != null)
+            {
+                _requiredParameterNotProvided(_rdgLogger, parameterTypeName, parameterName, source, null);
+            }
+        }
+
+        private static readonly Action<ILogger, string, string, string, Exception?> _requiredParameterNotProvided =
+            LoggerMessage.Define<string, string, string>(LogLevel.Debug, new EventId(4, "RequiredParameterNotProvided"), "Required parameter \"{ParameterType} {ParameterName}\" was not provided from {Source}.");
+
+        public void ImplicitBodyNotProvided(string parameterName)
+        {
+            if (_shouldThrow)
+            {
+                var message = string.Format(CultureInfo.InvariantCulture, "Implicit body inferred for parameter \"{0}\" but no body was provided. Did you mean to use a Service instead?", parameterName);
+                throw new BadHttpRequestException(message);
+            }
+
+            if (_rdgLogger != null)
+            {
+                _implicitBodyNotProvided(_rdgLogger, parameterName, null);
+            }
+        }
+
+        private static readonly Action<ILogger, string, Exception?> _implicitBodyNotProvided =
+            LoggerMessage.Define<string>(LogLevel.Debug, new EventId(5, "ImplicitBodyNotProvided"), "Implicit body inferred for parameter \"{ParameterName}\" but no body was provided. Did you mean to use a Service instead?");
+
+        public void UnexpectedJsonContentType(string? contentType)
+        {
+            if (_shouldThrow)
+            {
+                var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported JSON media type but got \"{0}\".", contentType);
+                throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType);
+            }
+
+            if (_rdgLogger != null)
+            {
+                _unexpectedJsonContentType(_rdgLogger, contentType ?? "(none)", null);
+            }
+        }
+
+        private static readonly Action<ILogger, string, Exception?> _unexpectedJsonContentType =
+            LoggerMessage.Define<string>(LogLevel.Debug, new EventId(6, "UnexpectedContentType"), "Expected a supported JSON media type but got \"{ContentType}\".");
+
+        public void UnexpectedNonFormContentType(string? contentType)
+        {
+            if (_shouldThrow)
+            {
+                var message = string.Format(CultureInfo.InvariantCulture, "Expected a supported form media type but got \"{0}\".", contentType);
+                throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType);
+            }
+
+            if (_rdgLogger != null)
+            {
+                _unexpectedNonFormContentType(_rdgLogger, contentType ?? "(none)", null);
+            }
+        }
+
+        private static readonly Action<ILogger, string, Exception?> _unexpectedNonFormContentType =
+            LoggerMessage.Define<string>(LogLevel.Debug, new EventId(7, "UnexpectedNonFormContentType"), "Expected a supported form media type but got \"{ContentType}\".");
+
+        public void InvalidFormRequestBody(string parameterTypeName, string parameterName, Exception exception)
+        {
+            if (_shouldThrow)
+            {
+                var message = string.Format(CultureInfo.InvariantCulture, "Failed to read parameter \"{0} {1}\" from the request body as form.", parameterTypeName, parameterName);
+                throw new BadHttpRequestException(message, exception);
+            }
+
+            if (_rdgLogger != null)
+            {
+                _invalidFormRequestBody(_rdgLogger, parameterTypeName, parameterName, exception);
+            }
+        }
+
+        private static readonly Action<ILogger, string, string, Exception?> _invalidFormRequestBody =
+            LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(8, "InvalidFormRequestBody"), "Failed to read parameter \"{ParameterType} {ParameterName}\" from the request body as form.");
+    }
+}

+ 106 - 0
src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.AsParameters.cs

@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Globalization;
+using Microsoft.AspNetCore.Http.RequestDelegateGenerator;
+using Microsoft.CodeAnalysis;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.AspNetCore.Http.Generators.Tests;
+
+public partial class CompileTimeCreationTests : RequestDelegateCreationTests
+{
+    [Theory]
+    [InlineData("ParameterListRecordStruct")]
+    [InlineData("ParameterListRecordClass")]
+    [InlineData("ParameterListStruct")]
+    [InlineData("ParameterListClass")]
+    public async Task RequestDelegateThrowsWhenNullableParameterList(string parameterType)
+    {
+        var source = $$"""
+void TestAction(HttpContext context, [AsParameters] {{parameterType}}? args)
+{
+    context.Items.Add("value", args);
+}
+app.MapGet("/", TestAction);
+""";
+
+        var (generatorRunResult, compilation) = await RunGeneratorAsync(source);
+
+        // Emits diagnostic but generates no source
+        var result = Assert.IsType<GeneratorRunResult>(generatorRunResult);
+        var diagnostic = Assert.Single(result.Diagnostics);
+        Assert.Equal(DiagnosticDescriptors.InvalidAsParametersNullable.Id, diagnostic.Id);
+        Assert.Empty(result.GeneratedSources);
+    }
+
+    public static object[][] BadArgumentListActions
+    {
+        get
+        {
+            static string GetAbstractTypeError(Type type)
+                => $"The abstract type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}' is not supported.";
+
+            static string GetMultipleContructorsError(Type type)
+                => $"Only a single public parameterized constructor is allowed for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'.";
+
+            static string GetNoContructorsError(Type type)
+                => $"No public parameterless constructor found for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'.";
+
+            static string GetInvalidConstructorError(Type type)
+                => $"The public parameterized constructor must contain only parameters that match the declared public properties for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'.";
+
+            return new []
+            {
+                    new object[] { "BadArgumentListRecord", DiagnosticDescriptors.InvalidAsParametersSingleConstructorOnly.Id, GetMultipleContructorsError(typeof(BadArgumentListRecord)) },
+                    new object[] { "BadArgumentListClass", DiagnosticDescriptors.InvalidAsParametersSignature.Id, GetInvalidConstructorError(typeof(BadArgumentListClass)) },
+                    new object[] { "BadArgumentListClassMultipleCtors", DiagnosticDescriptors.InvalidAsParametersSingleConstructorOnly.Id, GetMultipleContructorsError(typeof(BadArgumentListClassMultipleCtors))  },
+                    new object[] { "BadAbstractArgumentListClass", DiagnosticDescriptors.InvalidAsParametersAbstractType.Id, GetAbstractTypeError(typeof(BadAbstractArgumentListClass)) },
+                    new object[] { "BadNoPublicConstructorArgumentListClass", DiagnosticDescriptors.InvalidAsParametersNoConstructorFound.Id, GetNoContructorsError(typeof(BadNoPublicConstructorArgumentListClass)) },
+            };
+        }
+    }
+
+    [Theory]
+    [MemberData(nameof(BadArgumentListActions))]
+    public async Task BuildRequestDelegateEmitsDiagnosticForInvalidParameterListConstructor(
+        string parameterType,
+        string diagnosticId,
+         string message)
+    {
+        var source = $$"""
+void TestAction(HttpContext context, [AsParameters] {{parameterType}} args)
+{
+    context.Items.Add("value", args);
+}
+app.MapGet("/", TestAction);
+""";
+
+        var (generatorRunResult, _) = await RunGeneratorAsync(source);
+
+        // Emits diagnostic but generates no source
+        var result = Assert.IsType<GeneratorRunResult>(generatorRunResult);
+        var diagnostic = Assert.Single(result.Diagnostics);
+        Assert.Equal(diagnosticId, diagnostic.Id);
+        Assert.Equal(message, diagnostic.GetMessage(CultureInfo.InvariantCulture));
+        Assert.Empty(result.GeneratedSources);
+    }
+
+    [Theory]
+    [InlineData("NestedArgumentListRecord")]
+    [InlineData("ClassWithParametersConstructor")]
+    public async Task BuildRequestDelegateThrowsNotSupportedExceptionForNestedParametersList(string parameterType)
+    {
+        var source = $$"""
+void TestAction([AsParameters] {{parameterType}} req) { }
+app.MapGet("/", TestAction);
+""";
+        var (generatorRunResult, _) = await RunGeneratorAsync(source);
+
+        // Emits diagnostic but generates no source
+        var result = Assert.IsType<GeneratorRunResult>(generatorRunResult);
+        var diagnostic = Assert.Single(result.Diagnostics);
+        Assert.Equal(DiagnosticDescriptors.InvalidAsParametersNested.Id, diagnostic.Id);
+        Assert.Empty(result.GeneratedSources);
+    }
+}

+ 1 - 22
src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.cs

@@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Http.RequestDelegateGenerator;
 
 
 namespace Microsoft.AspNetCore.Http.Generators.Tests;
 namespace Microsoft.AspNetCore.Http.Generators.Tests;
 
 
-public class CompileTimeCreationTests : RequestDelegateCreationTests
+public partial class CompileTimeCreationTests : RequestDelegateCreationTests
 {
 {
     protected override bool IsGeneratorEnabled { get; } = true;
     protected override bool IsGeneratorEnabled { get; } = true;
 
 
@@ -58,27 +58,6 @@ app.MapGet("/hello", (HttpContext context) => Task.CompletedTask);
         Assert.Equal("'invalidName' is not a route parameter.", exception.Message);
         Assert.Equal("'invalidName' is not a route parameter.", exception.Message);
     }
     }
 
 
-    [Fact]
-    public async Task MapAction_WarnsForUnsupportedAsParametersAttribute()
-    {
-        var source = """app.MapGet("/{routeValue}", ([AsParameters] Todo todo) => todo);""";
-        var (generatorRunResult, compilation) = await RunGeneratorAsync(source);
-
-        // Emits diagnostic but generates no source
-        var result = Assert.IsType<GeneratorRunResult>(generatorRunResult);
-        var diagnostic = Assert.Single(result.Diagnostics);
-        Assert.Equal(DiagnosticDescriptors.UnableToResolveParameterDescriptor.Id, diagnostic.Id);
-        Assert.Empty(result.GeneratedSources);
-
-        // Falls back to runtime-generated endpoint
-        var endpoint = GetEndpointFromCompilation(compilation, false);
-
-        var httpContext = CreateHttpContext();
-        httpContext.Request.QueryString = new QueryString($"?Id=0&Name=Test&IsComplete=false");
-        await endpoint.RequestDelegate(httpContext);
-        await VerifyResponseBodyAsync(httpContext, """{"id":0,"name":"Test","isComplete":false}""");
-    }
-
     [Fact]
     [Fact]
     public async Task MapAction_WarnsForUnsupportedRouteVariable()
     public async Task MapAction_WarnsForUnsupportedRouteVariable()
     {
     {

+ 420 - 0
src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.AsParameters.cs

@@ -0,0 +1,420 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+using System.Globalization;
+using System.Security.Claims;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.Http.Generators.Tests;
+
+public partial class RequestDelegateCreationTests
+{
+    [Fact]
+    public async Task RequestDelegatePopulatesFromRouteParameterBased_FromParameterList()
+    {
+        const int originalRouteParam = 42;
+
+        var source = """
+static void TestAction([AsParameters] ParameterListFromRoute args)
+{
+    args.HttpContext.Items["input"] = args.Value;
+}
+app.MapGet("/{Value}", TestAction);
+""";
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var httpContext = CreateHttpContext();
+        httpContext.Request.RouteValues["value"] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo);
+
+        await endpoint.RequestDelegate(httpContext);
+
+        Assert.Equal(originalRouteParam, httpContext.Items["input"]);
+    }
+
+    [Fact]
+    public async Task RequestDelegatePopulatesFromQueryParameter_FromParameterList()
+    {
+        // QueryCollection is case sensitve, since we now getting
+        // the parameter name from the Property/Record constructor
+        // we should match the case here
+        const string paramName = "Value";
+        const int originalQueryParam = 42;
+
+        var source = """
+static void TestAction([AsParameters] ParameterListFromQuery args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+app.MapGet("/", TestAction);
+""";
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var query = new QueryCollection(new Dictionary<string, StringValues>()
+        {
+            [paramName] = originalQueryParam.ToString(NumberFormatInfo.InvariantInfo)
+        });
+
+        var httpContext = CreateHttpContext();
+        httpContext.Request.Query = query;
+
+        await endpoint.RequestDelegate(httpContext);
+
+        Assert.Equal(originalQueryParam, httpContext.Items["input"]);
+    }
+
+    [Fact]
+    public async Task RequestDelegatePopulatesFromHeaderParameter_FromParameterList()
+    {
+        const string customHeaderName = "X-Custom-Header";
+        const int originalHeaderParam = 42;
+
+        var source = """
+static void TestAction([AsParameters] ParameterListFromHeader args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+app.MapGet("/", TestAction);
+""";
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var httpContext = CreateHttpContext();
+        httpContext.Request.Headers[customHeaderName] = originalHeaderParam.ToString(NumberFormatInfo.InvariantInfo);
+
+        await endpoint.RequestDelegate(httpContext);
+
+        Assert.Equal(originalHeaderParam, httpContext.Items["input"]);
+    }
+
+    [Fact]
+    public async Task RequestDelegatePopulatesHttpContextParametersWithoutAttribute_FromParameterList()
+    {
+        var source = """
+static void TestAction([AsParameters] ParametersListWithHttpContext args)
+{
+    args.HttpContext.Items.Add("input", args.HttpContext);
+    args.HttpContext.Items.Add("user", args.User);
+    args.HttpContext.Items.Add("request", args.Request);
+    args.HttpContext.Items.Add("response", args.Response);
+}
+app.MapGet("/", TestAction);
+""";
+
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var httpContext = CreateHttpContext();
+        httpContext.User = new ClaimsPrincipal();
+
+        await endpoint.RequestDelegate(httpContext);
+
+        Assert.Same(httpContext, httpContext.Items["input"]);
+        Assert.Same(httpContext.User, httpContext.Items["user"]);
+        Assert.Same(httpContext.Request, httpContext.Items["request"]);
+        Assert.Same(httpContext.Response, httpContext.Items["response"]);
+    }
+
+    public static object[][] FromParameterListActions
+    {
+        get
+        {
+            var TestParameterListRecordStruct = """
+void TestAction([AsParameters] ParameterListRecordStruct args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+""";
+
+            var TestParameterListRecordClass = """
+void TestAction([AsParameters] ParameterListRecordClass args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+""";
+
+            var TestParameterListRecordWithoutPositionalParameters = """
+void TestAction([AsParameters] ParameterListRecordWithoutPositionalParameters args)
+{
+    args.HttpContext!.Items.Add("input", args.Value);
+}
+""";
+
+            var TestParameterListStruct = """
+void TestAction([AsParameters] ParameterListStruct args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+""";
+
+            var TestParameterListMutableStruct = """
+void TestAction([AsParameters] ParameterListMutableStruct args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+""";
+
+            var TestParameterListStructWithParameterizedContructor = """
+void TestAction([AsParameters] ParameterListStructWithParameterizedContructor args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+""";
+
+            var TestParameterListStructWithMultipleParameterizedContructor = """
+void TestAction([AsParameters] ParameterListStructWithMultipleParameterizedContructor args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+""";
+
+            var TestParameterListClass = """
+void TestAction([AsParameters] ParameterListClass args)
+{
+    args.HttpContext!.Items.Add("input", args.Value);
+}
+""";
+
+            var TestParameterListClassWithParameterizedContructor = """
+void TestAction([AsParameters] ParameterListClassWithParameterizedContructor args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+""";
+
+            return new[]
+            {
+                new object[] { TestParameterListRecordStruct },
+                new object[] { TestParameterListRecordClass },
+                new object[] { TestParameterListRecordWithoutPositionalParameters },
+                new object[] { TestParameterListStruct },
+                new object[] { TestParameterListMutableStruct },
+                new object[] { TestParameterListStructWithParameterizedContructor },
+                new object[] { TestParameterListStructWithMultipleParameterizedContructor },
+                new object[] { TestParameterListClass },
+                new object[] { TestParameterListClassWithParameterizedContructor },
+            };
+        }
+    }
+
+    [Theory]
+    [MemberData(nameof(FromParameterListActions))]
+    public async Task RequestDelegatePopulatesFromParameterList(string innerSource)
+    {
+        var source = $$"""
+{{innerSource}}
+app.MapGet("/{value}", TestAction);
+""";
+        const string paramName = "value";
+        const int originalRouteParam = 42;
+
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var httpContext = CreateHttpContext();
+        httpContext.Request.RouteValues[paramName] = originalRouteParam.ToString(NumberFormatInfo.InvariantInfo);
+
+        await endpoint.RequestDelegate(httpContext);
+
+        Assert.Equal(originalRouteParam, httpContext.Items["input"]);
+    }
+
+    [Fact]
+    public async Task RequestDelegatePopulatesFromParameterListUsesDefaultValue()
+    {
+        const int expectedValue = 42;
+        var source = """
+void TestAction([AsParameters] ParameterListWitDefaultValue args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+app.MapGet("/{value}", TestAction);
+""";
+        var httpContext = CreateHttpContext();
+
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        await endpoint.RequestDelegate(httpContext);
+
+        Assert.Equal(expectedValue, httpContext.Items["input"]);
+    }
+
+    [Fact]
+    public async Task VerifyAsParametersBaseline()
+    {
+        var (_, compilation) = await RunGeneratorAsync("""
+void parameterListWithDefaultValue([AsParameters] ParameterListWitDefaultValue args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+void parameterListRecordStruct([AsParameters] ParameterListRecordStruct args)
+{
+    args.HttpContext.Items.Add("input", args.Value);
+}
+static void parametersListWithHttpContext([AsParameters] ParametersListWithHttpContext args)
+{
+    args.HttpContext.Items.Add("input", args.HttpContext);
+    args.HttpContext.Items.Add("user", args.User);
+    args.HttpContext.Items.Add("request", args.Request);
+    args.HttpContext.Items.Add("response", args.Response);
+}
+static void parametersListWithMetadataType([AsParameters] ParametersListWithMetadataType args)
+{
+    args.HttpContext.Items.Add("value", args.Value);
+}
+app.MapGet("/parameterListWithDefaultValue/{value}", parameterListWithDefaultValue);
+app.MapGet("/parameterListRecordStruct/{value}", parameterListRecordStruct);
+app.MapGet("/parametersListWithHttpContext", parametersListWithHttpContext);
+app.MapPost("/parametersListWithImplicitFromBody", ([AsParameters] ParametersListWithImplicitFromBody args) => args.Todo.Name ?? string.Empty);
+app.MapGet("/parametersListWithMetadataType", parametersListWithMetadataType);
+""");
+
+        await VerifyAgainstBaselineUsingFile(compilation);
+    }
+
+    [Fact]
+    public async Task RequestDelegatePopulatesFromParameterListAndSkipReadOnlyProperties()
+    {
+        const int routeParamValue = 42;
+        var expectedInput = new ParameterListWithReadOnlyProperties() { Value = routeParamValue };
+
+        var source = """
+void TestAction(HttpContext context, [AsParameters] ParameterListWithReadOnlyProperties args)
+{
+    context.Items.Add("input", args);
+}
+app.MapGet("/{value}", TestAction);
+""";
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var httpContext = CreateHttpContext();
+
+        httpContext.Request.RouteValues[nameof(ParameterListWithReadOnlyProperties.Value)] = routeParamValue.ToString(NumberFormatInfo.InvariantInfo);
+        httpContext.Request.RouteValues[nameof(ParameterListWithReadOnlyProperties.ConstantValue)] = routeParamValue.ToString(NumberFormatInfo.InvariantInfo);
+        httpContext.Request.RouteValues[nameof(ParameterListWithReadOnlyProperties.ReadOnlyValue)] = routeParamValue.ToString(NumberFormatInfo.InvariantInfo);
+        httpContext.Request.RouteValues[nameof(ParameterListWithReadOnlyProperties.PrivateSetValue)] = routeParamValue.ToString(NumberFormatInfo.InvariantInfo);
+
+        await endpoint.RequestDelegate(httpContext);
+
+        var input = Assert.IsType<ParameterListWithReadOnlyProperties>(httpContext.Items["input"]);
+        Assert.Equal(expectedInput.Value, input.Value);
+        Assert.Equal(expectedInput.ConstantValue, input.ConstantValue);
+        Assert.Equal(expectedInput.ReadOnlyValue, input.ReadOnlyValue);
+        Assert.Equal(expectedInput.PrivateSetValue, input.PrivateSetValue);
+    }
+
+    [Fact]
+    public async Task RequestDelegatePopulatesFromMultipleParameterLists()
+    {
+        const int foo = 1;
+        const int bar = 2;
+
+        var source = """
+void TestAction(HttpContext context, [AsParameters] SampleParameterList args, [AsParameters] AdditionalSampleParameterList args2)
+{
+    context.Items.Add("foo", args.Foo);
+    context.Items.Add("bar", args2.Bar);
+}
+app.MapGet("/{foo}/{bar}", TestAction);
+""";
+
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var httpContext = CreateHttpContext();
+        httpContext.Request.RouteValues[nameof(SampleParameterList.Foo)] = foo.ToString(NumberFormatInfo.InvariantInfo);
+        httpContext.Request.RouteValues[nameof(AdditionalSampleParameterList.Bar)] = bar.ToString(NumberFormatInfo.InvariantInfo);
+
+        await endpoint.RequestDelegate(httpContext);
+
+        Assert.Equal(foo, httpContext.Items["foo"]);
+        Assert.Equal(bar, httpContext.Items["bar"]);
+    }
+
+    [Fact]
+    public async Task RequestDelegatePopulatesFromBindAsyncParameterList()
+    {
+        const string uriValue = "https://example.org/";
+
+        var source = """
+void TestAction([AsParameters] ParametersListWithBindAsyncType args)
+{
+    args.HttpContext.Items.Add("value", args.Value);
+    args.HttpContext.Items.Add("anotherValue", args.MyBindAsyncParam);
+}
+app.MapGet("/", TestAction);
+""";
+
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var httpContext = CreateHttpContext();
+        httpContext.Request.Headers.Referer = uriValue;
+
+        await endpoint.RequestDelegate(httpContext);
+
+        var value = Assert.IsType<InheritBindAsync>(httpContext.Items["value"]);
+        var anotherValue = Assert.IsType<MyBindAsyncRecord>(httpContext.Items["anotherValue"]);
+
+        Assert.Equal(uriValue, value.Uri?.ToString());
+        Assert.Equal(uriValue, anotherValue.Uri?.ToString());
+    }
+
+    [Fact]
+    public async Task RequestDelegatePopulatesFromMetadataProviderParameterList()
+    {
+        var source = """
+void TestAction([AsParameters] ParametersListWithMetadataType args)
+{
+    args.HttpContext.Items.Add("value", args.Value);
+}
+app.MapPost("/", TestAction);
+""";
+
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        Assert.Contains(endpoint.Metadata, m => m is ParameterNameMetadata { Name: "Value" });
+    }
+
+    [Fact]
+    public async Task RequestDelegateFactory_AsParameters_SupportsRequiredMember()
+    {
+        // Arrange
+        var source = """
+app.MapGet("/{requiredRouteParam}", TestAction);
+static void TestAction([AsParameters] ParameterListRequiredStringFromDifferentSources args) { }
+""";
+
+        var (_, compilation) = await RunGeneratorAsync(source);
+        var endpoint = GetEndpointFromCompilation(compilation);
+
+        var httpContext = CreateHttpContext();
+
+        // Act
+        await endpoint.RequestDelegate(httpContext);
+
+        // Assert that the required modifier on members that
+        // are not nullable treats them as required.
+        Assert.Equal(400, httpContext.Response.StatusCode);
+
+        var logs = TestSink.Writes.ToArray();
+
+        Assert.Equal(3, logs.Length);
+
+        Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
+        Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
+        Assert.Equal(@"Required parameter ""string RequiredRouteParam"" was not provided from route.", logs[0].Message);
+
+        Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[1].EventId);
+        Assert.Equal(LogLevel.Debug, logs[1].LogLevel);
+        Assert.Equal(@"Required parameter ""string RequiredQueryParam"" was not provided from query string.", logs[1].Message);
+
+        Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[2].EventId);
+        Assert.Equal(LogLevel.Debug, logs[2].LogLevel);
+        Assert.Equal(@"Required parameter ""string RequiredHeaderParam"" was not provided from header.", logs[2].Message);
+    }
+}

+ 2 - 0
src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.JsonBody.cs

@@ -35,6 +35,7 @@ IResult postTodoWithDefault([FromBody] Todo todo = null) => TypedResults.Ok(todo
 app.MapPost("/", postTodoWithDefault);
 app.MapPost("/", postTodoWithDefault);
 #nullable restore
 #nullable restore
 """;
 """;
+            var fromBodyAsParametersRequiredSource = """app.MapPost("/", ([AsParameters] ParametersListWithExplicitFromBody args) => TypedResults.Ok(args.Todo));""";
             var fromBodyRequiredWithFilterSource = $"""app.MapPost("/", ([FromBody] Todo todo) => TypedResults.Ok(todo)){withFilter}""";
             var fromBodyRequiredWithFilterSource = $"""app.MapPost("/", ([FromBody] Todo todo) => TypedResults.Ok(todo)){withFilter}""";
             var fromBodyEmptyBehaviorWithFilterSource = $"""app.MapPost("/", ([FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] Todo todo) => TypedResults.Ok(todo)){withFilter}""";
             var fromBodyEmptyBehaviorWithFilterSource = $"""app.MapPost("/", ([FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] Todo todo) => TypedResults.Ok(todo)){withFilter}""";
             var fromBodyAllowEmptyWithFilterSource = $"""app.MapPost("/", ([CustomFromBody(AllowEmpty = true)] Todo todo) => TypedResults.Ok(todo)){withFilter}""";
             var fromBodyAllowEmptyWithFilterSource = $"""app.MapPost("/", ([CustomFromBody(AllowEmpty = true)] Todo todo) => TypedResults.Ok(todo)){withFilter}""";
@@ -50,6 +51,7 @@ app.MapPost("/", postTodoWithDefault){withFilter}
             {
             {
                 new object[] { fromBodyRequiredSource, todo, 200, expectedBody },
                 new object[] { fromBodyRequiredSource, todo, 200, expectedBody },
                 new object[] { fromBodyRequiredSource, null, 400, string.Empty },
                 new object[] { fromBodyRequiredSource, null, 400, string.Empty },
+                new object[] { fromBodyAsParametersRequiredSource, todo, 200, expectedBody },
                 new object[] { fromBodyEmptyBodyBehaviorSource, todo, 200, expectedBody },
                 new object[] { fromBodyEmptyBodyBehaviorSource, todo, 200, expectedBody },
                 new object[] { fromBodyEmptyBodyBehaviorSource, null, 200, string.Empty },
                 new object[] { fromBodyEmptyBodyBehaviorSource, null, 200, string.Empty },
                 new object[] { fromBodyAllowEmptySource, todo, 200, expectedBody },
                 new object[] { fromBodyAllowEmptySource, todo, 200, expectedBody },

+ 3 - 0
src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.JsonBodyOrService.cs

@@ -25,6 +25,7 @@ public abstract partial class RequestDelegateCreationTests : RequestDelegateCrea
             var expectedServiceBody = "Produced from service!";
             var expectedServiceBody = "Produced from service!";
             var implicitRequiredServiceSource = $"""app.MapPost("/", ({typeof(TestService)} svc) => svc.TestServiceMethod());""";
             var implicitRequiredServiceSource = $"""app.MapPost("/", ({typeof(TestService)} svc) => svc.TestServiceMethod());""";
             var implicitRequiredJsonBodySource = $"""app.MapPost("/", (Todo todo) => todo.Name ?? string.Empty);""";
             var implicitRequiredJsonBodySource = $"""app.MapPost("/", (Todo todo) => todo.Name ?? string.Empty);""";
+            var implicitRequiredJsonBodyViaAsParametersSource = $"""app.MapPost("/", ([AsParameters] ParametersListWithImplicitFromBody args) => args.Todo.Name ?? string.Empty);""";
 
 
             return new[]
             return new[]
             {
             {
@@ -32,6 +33,8 @@ public abstract partial class RequestDelegateCreationTests : RequestDelegateCrea
                 new object[] { implicitRequiredServiceSource, false, null, false, 400, string.Empty },
                 new object[] { implicitRequiredServiceSource, false, null, false, 400, string.Empty },
                 new object[] { implicitRequiredJsonBodySource, true, todo, false, 200, expectedTodoBody },
                 new object[] { implicitRequiredJsonBodySource, true, todo, false, 200, expectedTodoBody },
                 new object[] { implicitRequiredJsonBodySource, true, null, false, 400, string.Empty },
                 new object[] { implicitRequiredJsonBodySource, true, null, false, 400, string.Empty },
+                new object[] { implicitRequiredJsonBodyViaAsParametersSource, true, todo, false, 200, expectedTodoBody },
+                new object[] { implicitRequiredJsonBodyViaAsParametersSource, true, null, false, 400, string.Empty },
             };
             };
         }
         }
     }
     }

+ 12 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Logging.cs

@@ -17,11 +17,12 @@ public abstract partial class RequestDelegateCreationTests : RequestDelegateCrea
     public async Task RequestDelegateLogsStringValuesFromExplicitQueryStringSourceForUnpresentedValuesFailuresAsDebugAndSets400Response()
     public async Task RequestDelegateLogsStringValuesFromExplicitQueryStringSourceForUnpresentedValuesFailuresAsDebugAndSets400Response()
     {
     {
         var source = """
         var source = """
-app.MapGet("/", (
+app.MapGet("/{baz}", (
     HttpContext httpContext,
     HttpContext httpContext,
     [FromHeader(Name = "foo")] StringValues headerValues,
     [FromHeader(Name = "foo")] StringValues headerValues,
     [FromQuery(Name = "bar")] StringValues queryValues,
     [FromQuery(Name = "bar")] StringValues queryValues,
-    [FromForm(Name = "form")] StringValues formValues
+    [FromForm(Name = "form")] StringValues formValues,
+    [FromRoute(Name = "baz")] string routeValues
 ) =>
 ) =>
 {
 {
     httpContext.Items["invoked"] = true;
     httpContext.Items["invoked"] = true;
@@ -43,7 +44,7 @@ app.MapGet("/", (
 
 
         var logs = TestSink.Writes.ToArray();
         var logs = TestSink.Writes.ToArray();
 
 
-        Assert.Equal(3, logs.Length);
+        Assert.Equal(4, logs.Length);
 
 
         Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
         Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
         Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
         Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
@@ -68,6 +69,14 @@ app.MapGet("/", (
         Assert.Equal("StringValues", log3Values[0].Value);
         Assert.Equal("StringValues", log3Values[0].Value);
         Assert.Equal("formValues", log3Values[1].Value);
         Assert.Equal("formValues", log3Values[1].Value);
         Assert.Equal("form", log3Values[2].Value);
         Assert.Equal("form", log3Values[2].Value);
+
+        Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[3].EventId);
+        Assert.Equal(LogLevel.Debug, logs[3].LogLevel);
+        Assert.Equal(@"Required parameter ""string routeValues"" was not provided from route.", logs[3].Message);
+        var log4Values = Assert.IsAssignableFrom<IReadOnlyList<KeyValuePair<string, object>>>(logs[3].State);
+        Assert.Equal("string", log4Values[0].Value);
+        Assert.Equal("routeValues", log4Values[1].Value);
+        Assert.Equal("route", log4Values[2].Value);
     }
     }
 
 
     [Fact]
     [Fact]

+ 224 - 3
src/Http/Http.Extensions/test/RequestDelegateGenerator/SharedTypes.cs

@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // The .NET Foundation licenses this file to you under the MIT license.
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Reflection;
 using System.Reflection;
+using System.Security.Claims;
 using System.Text.Json;
 using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
 using System.Security.Cryptography.X509Certificates;
 using System.Security.Cryptography.X509Certificates;
@@ -9,6 +10,7 @@ using Microsoft.AspNetCore.Http.Features;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http.Metadata;
 using Microsoft.AspNetCore.Http.Metadata;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Routing;
 using Microsoft.AspNetCore.Routing;
 
 
 namespace Microsoft.AspNetCore.Http.Generators.Tests;
 namespace Microsoft.AspNetCore.Http.Generators.Tests;
@@ -204,7 +206,8 @@ public record MyBindAsyncRecord(Uri Uri)
         {
         {
             throw new UnreachableException($"Unexpected parameter type: {parameter.ParameterType}");
             throw new UnreachableException($"Unexpected parameter type: {parameter.ParameterType}");
         }
         }
-        if (parameter.Name?.StartsWith("myBindAsyncParam", StringComparison.Ordinal) == false)
+        if (parameter.Name?.StartsWith("myBindAsyncParam", StringComparison.OrdinalIgnoreCase
+        ) == false)
         {
         {
             throw new UnreachableException("Unexpected parameter name");
             throw new UnreachableException("Unexpected parameter name");
         }
         }
@@ -463,7 +466,7 @@ public struct BodyStruct
 
 
 public class ExceptionThrowingRequestBodyStream : Stream
 public class ExceptionThrowingRequestBodyStream : Stream
 {
 {
-    private readonly Exception _exceptionToThrow;
+    public readonly Exception _exceptionToThrow;
 
 
     public ExceptionThrowingRequestBodyStream(Exception exceptionToThrow)
     public ExceptionThrowingRequestBodyStream(Exception exceptionToThrow)
     {
     {
@@ -508,7 +511,7 @@ public class ExceptionThrowingRequestBodyStream : Stream
 
 
 public readonly struct TraceIdentifier
 public readonly struct TraceIdentifier
 {
 {
-    private TraceIdentifier(string id)
+    public TraceIdentifier(string id)
     {
     {
         Id = id;
         Id = id;
     }
     }
@@ -676,6 +679,224 @@ public class AccessesServicesMetadataBinder : IEndpointMetadataProvider
 }
 }
 
 
 public record MetadataService;
 public record MetadataService;
+public record ParameterListFromQuery(HttpContext HttpContext, [FromQuery] int Value);
+public record ParameterListFromRoute(HttpContext HttpContext, int Value);
+public record ParameterListFromHeader(HttpContext HttpContext, [FromHeader(Name = "X-Custom-Header")] int Value);
+public record ParametersListWithImplicitFromBody(HttpContext HttpContext, TodoStruct Todo);
+public record struct TodoStruct(int Id, string Name, bool IsComplete, TodoStatus Status) : ITodo;
+public record ParametersListWithExplicitFromBody(HttpContext HttpContext, [FromBody] Todo Todo);
+public record ParametersListWithHttpContext(
+    HttpContext HttpContext,
+    ClaimsPrincipal User,
+    HttpRequest Request,
+    HttpResponse Response);
+
+public record struct ParameterListRecordStruct(HttpContext HttpContext, [FromRoute] int Value);
+
+public record ParameterListRecordClass(HttpContext HttpContext, [FromRoute] int Value);
+
+#nullable enable
+public record ParameterListRecordWithoutPositionalParameters
+{
+    public HttpContext? HttpContext { get; set; }
+
+    [FromRoute]
+    public required int Value { get; set; }
+}
+#nullable restore
+
+public struct ParameterListStruct
+{
+    public HttpContext HttpContext { get; set; }
+
+    [FromRoute]
+    public int Value { get; set; }
+}
+
+public struct ParameterListMutableStruct
+{
+    public ParameterListMutableStruct()
+    {
+        Value = -1;
+        HttpContext = default!;
+    }
+
+    public HttpContext HttpContext { get; set; }
+
+    [FromRoute]
+    public int Value { get; set; }
+}
+
+public class ParameterListStructWithParameterizedContructor
+{
+    public ParameterListStructWithParameterizedContructor(HttpContext httpContext)
+    {
+        HttpContext = httpContext;
+        Value = 42;
+    }
+
+    public HttpContext HttpContext { get; set; }
+
+    public int Value { get; set; }
+}
+
+public struct ParameterListStructWithMultipleParameterizedContructor
+{
+    public ParameterListStructWithMultipleParameterizedContructor(HttpContext httpContext)
+    {
+        HttpContext = httpContext;
+        Value = 10;
+    }
+
+    public ParameterListStructWithMultipleParameterizedContructor(HttpContext httpContext, [FromHeader(Name = "Value")] int value)
+    {
+        HttpContext = httpContext;
+        Value = value;
+    }
+
+    public HttpContext HttpContext { get; set; }
+
+    [FromRoute]
+    public int Value { get; set; }
+}
+
+#nullable enable
+public class ParameterListClass
+{
+    public HttpContext? HttpContext { get; set; }
+
+    [FromRoute]
+    public int Value { get; set; }
+}
+#nullable restore
+
+public class ParameterListClassWithParameterizedContructor
+{
+    public ParameterListClassWithParameterizedContructor(HttpContext httpContext)
+    {
+        HttpContext = httpContext;
+        Value = 42;
+    }
+
+    public HttpContext HttpContext { get; set; }
+
+    public int Value { get; set; }
+}
+
+public class ParameterListWitDefaultValue
+{
+    public ParameterListWitDefaultValue(HttpContext httpContext, [FromRoute] int value = 42)
+    {
+        HttpContext = httpContext;
+        Value = value;
+    }
+
+    public HttpContext HttpContext { get; }
+    public int Value { get; }
+}
+
+public class ParameterListWithReadOnlyProperties
+{
+    public ParameterListWithReadOnlyProperties()
+    {
+        ReadOnlyValue = 1;
+        PrivateSetValue = 2;
+    }
+
+    public int Value { get; set; }
+
+    public int ConstantValue => 1;
+
+    public int ReadOnlyValue { get; }
+
+    public int PrivateSetValue { get; private set;  }
+}
+
+public record struct SampleParameterList(int Foo);
+public record struct AdditionalSampleParameterList(int Bar);
+
+public record ParametersListWithBindAsyncType(
+    HttpContext HttpContext,
+    InheritBindAsync Value,
+    MyBindAsyncRecord MyBindAsyncParam);
+
+public record ParametersListWithMetadataType(
+    HttpContext HttpContext,
+    AddsCustomParameterMetadataAsProperty Value);
+
+public class ParameterListRequiredStringFromDifferentSources
+{
+    public HttpContext HttpContext { get; set; }
+
+    [FromRoute]
+    public required string RequiredRouteParam { get; set; }
+
+    [FromQuery]
+    public required string RequiredQueryParam { get; set; }
+
+    [FromHeader]
+    public required string RequiredHeaderParam { get; set; }
+}
+
+public record BadArgumentListRecord(int Foo)
+{
+    public BadArgumentListRecord(int foo, int bar)
+        : this(foo)
+    {
+    }
+
+    public int Bar { get; set; }
+}
+
+public class BadNoPublicConstructorArgumentListClass
+{
+    private BadNoPublicConstructorArgumentListClass()
+    { }
+
+    public int Foo { get; set; }
+}
+
+public abstract class BadAbstractArgumentListClass
+{
+    public int Foo { get; set; }
+}
+
+public class BadArgumentListClass
+{
+    public BadArgumentListClass(int foo, string name)
+    {
+    }
+
+    public int Foo { get; set; }
+    public int Bar { get; set; }
+}
+
+public class BadArgumentListClassMultipleCtors
+{
+    public BadArgumentListClassMultipleCtors(int foo)
+    {
+    }
+
+    public BadArgumentListClassMultipleCtors(int foo, int bar)
+    {
+    }
+
+    public int Foo { get; set; }
+    public int Bar { get; set; }
+}
+
+public record NestedArgumentListRecord([AsParameters] object NestedParameterList);
+
+public class ClassWithParametersConstructor
+{
+    public ClassWithParametersConstructor([AsParameters] object nestedParameterList)
+    {
+        NestedParameterList = nestedParameterList;
+    }
+
+    public object NestedParameterList { get; set; }
+}
+
 public class CountsDefaultEndpointMetadataResult : IEndpointMetadataProvider, IResult
 public class CountsDefaultEndpointMetadataResult : IEndpointMetadataProvider, IResult
 {
 {
     public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
     public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)

+ 1 - 1
src/Shared/PropertyAsParameterInfo.cs

@@ -22,7 +22,7 @@ internal sealed class PropertyAsParameterInfo : ParameterInfo
 
 
     public PropertyAsParameterInfo(PropertyInfo propertyInfo, NullabilityInfoContext? nullabilityContext = null)
     public PropertyAsParameterInfo(PropertyInfo propertyInfo, NullabilityInfoContext? nullabilityContext = null)
     {
     {
-        Debug.Assert(null != propertyInfo);
+        Debug.Assert(propertyInfo != null, "PropertyInfo must be provided.");
 
 
         AttrsImpl = (ParameterAttributes)propertyInfo.Attributes;
         AttrsImpl = (ParameterAttributes)propertyInfo.Attributes;
         NameImpl = propertyInfo.Name;
         NameImpl = propertyInfo.Name;

+ 56 - 2
src/Shared/RoslynUtils/SymbolExtensions.cs

@@ -4,8 +4,10 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.Immutable;
 using System.Collections.Immutable;
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Linq;
+using System.Reflection.PortableExecutable;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.CodeAnalysis.CSharp;
 
 
@@ -59,12 +61,25 @@ internal static class SymbolExtensions
         return false;
         return false;
     }
     }
 
 
+    public static bool HasAttribute(this ImmutableArray<AttributeData> attributes, INamedTypeSymbol attributeType)
+    {
+        foreach (var attributeData in attributes)
+        {
+            if (SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, attributeType))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     public static bool HasAttributeImplementingInterface(this ISymbol symbol, INamedTypeSymbol interfaceType)
     public static bool HasAttributeImplementingInterface(this ISymbol symbol, INamedTypeSymbol interfaceType)
     {
     {
-        return symbol.HasAttributeImplementingInterface(interfaceType, out var _);
+        return symbol.TryGetAttributeImplementingInterface(interfaceType, out var _);
     }
     }
 
 
-    public static bool HasAttributeImplementingInterface(this ISymbol symbol, INamedTypeSymbol interfaceType, [NotNullWhen(true)] out AttributeData? matchedAttribute)
+    public static bool TryGetAttributeImplementingInterface(this ISymbol symbol, INamedTypeSymbol interfaceType, [NotNullWhen(true)] out AttributeData? matchedAttribute)
     {
     {
         foreach (var attributeData in symbol.GetAttributes())
         foreach (var attributeData in symbol.GetAttributes())
         {
         {
@@ -79,6 +94,26 @@ internal static class SymbolExtensions
         return false;
         return false;
     }
     }
 
 
+    public static bool HasAttributeImplementingInterface(this ImmutableArray<AttributeData> attributes, INamedTypeSymbol interfaceType)
+    {
+        return attributes.TryGetAttributeImplementingInterface(interfaceType, out var _);
+    }
+
+    public static bool TryGetAttributeImplementingInterface(this ImmutableArray<AttributeData> attributes, INamedTypeSymbol interfaceType, [NotNullWhen(true)] out AttributeData? matchedAttribute)
+    {
+        foreach (var attributeData in attributes)
+        {
+            if (attributeData.AttributeClass is not null && attributeData.AttributeClass.Implements(interfaceType))
+            {
+                matchedAttribute = attributeData;
+                return true;
+            }
+        }
+
+        matchedAttribute = null;
+        return false;
+    }
+
     public static bool Implements(this ITypeSymbol type, ITypeSymbol interfaceType)
     public static bool Implements(this ITypeSymbol type, ITypeSymbol interfaceType)
     {
     {
         foreach (var t in type.AllInterfaces)
         foreach (var t in type.AllInterfaces)
@@ -124,6 +159,12 @@ internal static class SymbolExtensions
             NullableAnnotation: NullableAnnotation.Annotated
             NullableAnnotation: NullableAnnotation.Annotated
         } || parameterSymbol.HasExplicitDefaultValue;
         } || parameterSymbol.HasExplicitDefaultValue;
 
 
+    public static bool IsOptional(this IPropertySymbol propertySymbol) =>
+        propertySymbol.Type is INamedTypeSymbol
+        {
+            NullableAnnotation: NullableAnnotation.Annotated
+        } && !propertySymbol.IsRequired;
+
     public static string GetDefaultValueString(this IParameterSymbol parameterSymbol)
     public static string GetDefaultValueString(this IParameterSymbol parameterSymbol)
     {
     {
         return !parameterSymbol.HasExplicitDefaultValue
         return !parameterSymbol.HasExplicitDefaultValue
@@ -145,4 +186,17 @@ internal static class SymbolExtensions
         }
         }
         return false;
         return false;
     }
     }
+
+    public static string GetParameterInfoFromConstructorCode(this IParameterSymbol parameterSymbol)
+    {
+        if (parameterSymbol is { ContainingSymbol: IMethodSymbol constructor })
+        {
+            var constructedType = $"typeof({parameterSymbol.ContainingType.ToDisplayString()})";
+            var parameterTypes = constructor.Parameters.Select(parameter => $"typeof({parameter.Type.ToDisplayString()})");
+            var parameterTypesString = string.Join(", ", parameterTypes);
+            var getConstructorParameters = $$"""new[] { {{parameterTypesString}} }""";
+            return $"{constructedType}.GetConstructor({getConstructorParameters})?.GetParameters()[{parameterSymbol.Ordinal}]";
+        }
+        return "null";
+    }
 }
 }