diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs
index dca41b3f155bdc40ab2b3a738db247407facce30..c8ae7b48af73d9318a9b02d01e6f8b315b98ab7c 100644
--- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs
+++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs
@@ -183,7 +183,7 @@ internal sealed class EndpointMetadataApiDescriptionProvider : IApiDescriptionPr
         var nullabilityContext = new NullabilityInfoContext();
         var nullability = nullabilityContext.Create(parameter);
         var isOptional = parameter.HasDefaultValue || nullability.ReadState != NullabilityState.NotNull || allowEmpty;
-        var parameterDescriptor = CreateParameterDescriptor(parameter);
+        var parameterDescriptor = CreateParameterDescriptor(parameter, pattern);
         var routeInfo = CreateParameterRouteInfo(pattern, parameter, isOptional);
 
         return new ApiParameterDescription
@@ -199,13 +199,17 @@ internal sealed class EndpointMetadataApiDescriptionProvider : IApiDescriptionPr
         };
     }
 
-    private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo parameter)
-        => new EndpointParameterDescriptor
+    private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo parameter, RoutePattern pattern)
+    {
+        var parameterName = parameter.Name ?? string.Empty;
+        var name = pattern.GetParameter(parameterName)?.Name ?? parameterName;
+        return new EndpointParameterDescriptor
         {
-            Name = parameter.Name ?? string.Empty,
+            Name = name,
             ParameterInfo = parameter,
             ParameterType = parameter.ParameterType,
         };
+    }
 
     private ApiParameterRouteInfo? CreateParameterRouteInfo(RoutePattern pattern, ParameterInfo parameter, bool isOptional)
     {
@@ -250,7 +254,9 @@ internal sealed class EndpointMetadataApiDescriptionProvider : IApiDescriptionPr
 
         if (attributes.OfType<IFromRouteMetadata>().FirstOrDefault() is { } routeAttribute)
         {
-            return (BindingSource.Path, routeAttribute.Name ?? parameter.Name ?? string.Empty, false, parameter.ParameterType);
+            var parameterName = parameter.Name ?? string.Empty;
+            var name = pattern.GetParameter(parameterName)?.Name ?? parameterName;
+            return (BindingSource.Path, routeAttribute.Name ?? name, false, parameter.ParameterType);
         }
         else if (attributes.OfType<IFromQueryMetadata>().FirstOrDefault() is { } queryAttribute)
         {
@@ -285,9 +291,9 @@ internal sealed class EndpointMetadataApiDescriptionProvider : IApiDescriptionPr
             var displayType = !parameter.ParameterType.IsPrimitive && Nullable.GetUnderlyingType(parameter.ParameterType)?.IsPrimitive != true
                 ? typeof(string) : parameter.ParameterType;
             // Path vs query cannot be determined by RequestDelegateFactory at startup currently because of the layering, but can be done here.
-            if (parameter.Name is { } name && pattern.GetParameter(name) is not null)
+            if (parameter.Name is { } name && pattern.GetParameter(name) is { } routeParam)
             {
-                return (BindingSource.Path, name, false, displayType);
+                return (BindingSource.Path, routeParam.Name, false, displayType);
             }
             else
             {
diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs
index 88f4f06e74f2b5a565b835f0a726d979a972d1ef..d3a8fc96eab1b16d5e77e7a1ab78d53120608a34 100644
--- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs
+++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs
@@ -450,13 +450,13 @@ public class EndpointMetadataApiDescriptionProviderTest
     [Fact]
     public void AddsMultipleParametersFromParametersAttribute()
     {
-        static void AssertParameters(ApiDescription apiDescription)
+        static void AssertParameters(ApiDescription apiDescription, string capturedName = "Foo")
         {
             Assert.Collection(
                 apiDescription.ParameterDescriptions,
                 param =>
                 {
-                    Assert.Equal("Foo", param.Name);
+                    Assert.Equal(capturedName, param.Name);
                     Assert.Equal(typeof(int), param.ModelMetadata.ModelType);
                     Assert.Equal(BindingSource.Path, param.Source);
                     Assert.True(param.IsRequired);
@@ -485,7 +485,7 @@ public class EndpointMetadataApiDescriptionProviderTest
         AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecord req) => { }));
         AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordStruct req) => { }));
         AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutPositionalParameters req) => { }));
-        AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{foo}"));
+        AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{foo}"), "foo");
         AssertParameters(GetApiDescription(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{Foo}"));
     }
 
@@ -1250,6 +1250,34 @@ public class EndpointMetadataApiDescriptionProviderTest
         Assert.Equal("A summary", summaryMetadata.Summary);
     }
 
+    [Theory]
+    [InlineData("/todos/{id}", "id")]
+    [InlineData("/todos/{Id}", "Id")]
+    [InlineData("/todos/{id:minlen(2)}", "id")]
+    public void FavorsParameterCasingInRoutePattern(string pattern, string expectedName)
+    {
+        var builder = CreateBuilder();
+        builder.MapGet(pattern, (int Id) => "");
+
+        var context = new ApiDescriptionProviderContext(Array.Empty<ActionDescriptor>());
+
+        var endpointDataSource = builder.DataSources.OfType<EndpointDataSource>().Single();
+        var hostEnvironment = new HostEnvironment
+        {
+            ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest)
+        };
+        var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource);
+
+        // Act
+        provider.OnProvidersExecuting(context);
+
+        // Assert
+        var apiDescription = Assert.Single(context.Results);
+        var parameter = Assert.Single(apiDescription.ParameterDescriptions);
+        Assert.Equal(expectedName, parameter.Name);
+        Assert.Equal(expectedName, parameter.ParameterDescriptor.Name);
+    }
+
     private static IEnumerable<string> GetSortedMediaTypes(ApiResponseType apiResponseType)
     {
         return apiResponseType.ApiResponseFormats
diff --git a/src/OpenApi/src/OpenApiGenerator.cs b/src/OpenApi/src/OpenApiGenerator.cs
index 1683af0bd528047a2cc9504bbd06bb2415f5dc78..4936bfba13ce9e4bbfc191d230df1a38fe53e80d 100644
--- a/src/OpenApi/src/OpenApiGenerator.cs
+++ b/src/OpenApi/src/OpenApiGenerator.cs
@@ -354,6 +354,11 @@ internal sealed class OpenApiGenerator
 
         foreach (var parameter in parameters)
         {
+            if (parameter.Name is null)
+            {
+                throw new InvalidOperationException($"Encountered a parameter of type '{parameter.ParameterType}' without a name. Parameters must have a name.");
+            }
+
             var (isBodyOrFormParameter, parameterLocation) = GetOpenApiParameterLocation(parameter, pattern, disableInferredBody);
 
             // If the parameter isn't something that would be populated in RequestBody
@@ -367,9 +372,10 @@ internal sealed class OpenApiGenerator
             var nullabilityContext = new NullabilityInfoContext();
             var nullability = nullabilityContext.Create(parameter);
             var isOptional = parameter.HasDefaultValue || nullability.ReadState != NullabilityState.NotNull;
+            var name = pattern.GetParameter(parameter.Name) is { } routeParameter ? routeParameter.Name : parameter.Name;
             var openApiParameter = new OpenApiParameter()
             {
-                Name = parameter.Name,
+                Name =  name,
                 In = parameterLocation,
                 Content = GetOpenApiParameterContent(metadata),
                 Schema = OpenApiSchemaGenerator.GetOpenApiSchema(parameter.ParameterType),
diff --git a/src/OpenApi/test/OpenApiGeneratorTests.cs b/src/OpenApi/test/OpenApiGeneratorTests.cs
index 42e00cffa700b9ea081265b0ccdb83041428d444..1d9b84f359a0a05da9888b3377a84818342cbf0a 100644
--- a/src/OpenApi/test/OpenApiGeneratorTests.cs
+++ b/src/OpenApi/test/OpenApiGeneratorTests.cs
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Linq.Expressions;
 using System.Reflection;
 using System.Security.Claims;
 using Microsoft.AspNetCore.Http;
@@ -49,6 +50,15 @@ public class OpenApiOperationGeneratorTests
         Assert.Equal(declaringTypeName, tag.Name);
     }
 
+    [Fact]
+    public void ThrowsInvalidOperationExceptionGivenUnnamedParameter()
+    {
+        var unnamedParameter = Expression.Parameter(typeof(int));
+        var lambda = Expression.Lambda(Expression.Block(), unnamedParameter);
+        var ex = Assert.Throws<InvalidOperationException>(() => GetOpenApiOperation(lambda.Compile()));
+        Assert.Equal("Encountered a parameter of type 'System.Runtime.CompilerServices.Closure' without a name. Parameters must have a name.", ex.Message);
+    }
+
     [Fact]
     public void AddsRequestFormatFromMetadata()
     {
@@ -357,13 +367,13 @@ public class OpenApiOperationGeneratorTests
     [Fact]
     public void AddsMultipleParametersFromParametersAttribute()
     {
-        static void AssertParameters(OpenApiOperation operation)
+        static void AssertParameters(OpenApiOperation operation, string capturedName = "Foo")
         {
             Assert.Collection(
                 operation.Parameters,
                 param =>
                 {
-                    Assert.Equal("Foo", param.Name);
+                    Assert.Equal(capturedName, param.Name);
                     Assert.Equal("integer", param.Schema.Type);
                     Assert.Equal(ParameterLocation.Path, param.In);
                     Assert.True(param.Required);
@@ -391,7 +401,7 @@ public class OpenApiOperationGeneratorTests
         AssertParameters(GetOpenApiOperation(([AsParameters] ArgumentListRecord req) => { }));
         AssertParameters(GetOpenApiOperation(([AsParameters] ArgumentListRecordStruct req) => { }));
         AssertParameters(GetOpenApiOperation(([AsParameters] ArgumentListRecordWithoutPositionalParameters req) => { }));
-        AssertParameters(GetOpenApiOperation(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{foo}"));
+        AssertParameters(GetOpenApiOperation(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{foo}"), "foo");
         AssertParameters(GetOpenApiOperation(([AsParameters] ArgumentListRecordWithoutAttributes req) => { }, "/{Foo}"));
     }
 
@@ -787,6 +797,19 @@ public class OpenApiOperationGeneratorTests
         Assert.Equal("object", content.Value.Schema.Type);
         Assert.Equal("200", response.Key);
         Assert.Equal("application/json", content.Key);
+
+    }
+
+    [Theory]
+    [InlineData("/todos/{id}", "id")]
+    [InlineData("/todos/{Id}", "Id")]
+    [InlineData("/todos/{id:minlen(2)}", "id")]
+    public void FavorsParameterCasingInRoutePattern(string pattern, string expectedName)
+    {
+        var operation = GetOpenApiOperation((int Id) => "", pattern);
+
+        var param = Assert.Single(operation.Parameters);
+        Assert.Equal(expectedName, param.Name);
     }
 
     private static OpenApiOperation GetOpenApiOperation(
diff --git a/src/Shared/PropertyAsParameterInfo.cs b/src/Shared/PropertyAsParameterInfo.cs
index 31e96c3b8b7b0426ab0325adbe16d62ac84d39ee..aa2a29314bd098a23d1f5d04b6b5d39b00041469 100644
--- a/src/Shared/PropertyAsParameterInfo.cs
+++ b/src/Shared/PropertyAsParameterInfo.cs
@@ -74,6 +74,11 @@ internal sealed class PropertyAsParameterInfo : ParameterInfo
 
         for (var i = 0; i < parameters.Length; i++)
         {
+            if (parameters[i].Name is null)
+            {
+                throw new InvalidOperationException($"Encountered a parameter of type '{parameters[i].ParameterType}' without a name. Parameters must have a name.");
+            }
+
             if (parameters[i].CustomAttributes.Any(a => a.AttributeType == typeof(AsParametersAttribute)))
             {
                 // Initialize the list with all parameter already processed