From 0086a69b5920c30857606ecc94ff516b14d21559 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:34:26 +0000 Subject: [PATCH 1/3] Initial plan From 1390e939aae260961ce619d1e059de2a70b62778 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:41:59 +0000 Subject: [PATCH 2/3] Fix: optional non-nullable struct query params with = default throw in RequestDelegateFactory Agent-Logs-Url: https://github.com/dotnet/aspnetcore/sessions/b06c1d39-20b4-4060-bca6-72f07cbf08ad Co-authored-by: BrennanConroy <7574801+BrennanConroy@users.noreply.github.com> --- .../src/RequestDelegateFactory.cs | 9 +++ .../test/RequestDelegateFactoryTests.cs | 60 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index c51e7aae5a36..9b0c6d21bdf8 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -1986,6 +1986,15 @@ private static Expression CreateDefaultValueExpression(object? defaultValue, Typ { if (defaultValue is null) { + // For non-nullable value types (e.g., Guid, DateTime, TimeSpan), reflection reports + // DefaultValue == null when the parameter default is `= default`. Using + // Expression.Constant(null, valueType) would throw an ArgumentException because null + // is not valid for a non-nullable value type. Use Expression.Default instead. + if (parameterType.IsValueType && Nullable.GetUnderlyingType(parameterType) is null) + { + return Expression.Default(parameterType); + } + return Expression.Constant(null, parameterType); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 61e7d2a46965..6d17786aa43f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -1849,6 +1849,66 @@ public async Task CanSetParseableStringParamAsOptionalWithNullabilityDisability( Assert.Equal(expectedResponse, decodedResponseBody); } + [Theory] + [InlineData(true, "Id: a0e1f2b3-c4d5-4e6f-7a8b-9c0d1e2f3a4b")] + [InlineData(false, "Id: 00000000-0000-0000-0000-000000000000")] + public async Task CanSetGuidParamAsOptionalWithDefaultValue(bool provideValue, string expectedResponse) + { + string optionalQueryParam(Guid id = default) => $"Id: {id}"; + + var httpContext = CreateHttpContext(); + var responseBodyStream = new MemoryStream(); + httpContext.Response.Body = responseBodyStream; + + if (provideValue) + { + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["id"] = "a0e1f2b3-c4d5-4e6f-7a8b-9c0d1e2f3a4b" + }); + } + + var factoryResult = RequestDelegateFactory.Create(optionalQueryParam); + var requestDelegate = factoryResult.RequestDelegate; + + await requestDelegate(httpContext); + + Assert.Equal(200, httpContext.Response.StatusCode); + Assert.False(httpContext.RequestAborted.IsCancellationRequested); + var decodedResponseBody = Encoding.UTF8.GetString(responseBodyStream.ToArray()); + Assert.Equal(expectedResponse, decodedResponseBody); + } + + [Theory] + [InlineData(true, "Date: 2024-01-15T00:00:00.0000000")] + [InlineData(false, "Date: 0001-01-01T00:00:00.0000000")] + public async Task CanSetDateTimeParamAsOptionalWithDefaultValue(bool provideValue, string expectedResponse) + { + string optionalQueryParam(DateTime date = default) => $"Date: {date:O}"; + + var httpContext = CreateHttpContext(); + var responseBodyStream = new MemoryStream(); + httpContext.Response.Body = responseBodyStream; + + if (provideValue) + { + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["date"] = "2024-01-15" + }); + } + + var factoryResult = RequestDelegateFactory.Create(optionalQueryParam); + var requestDelegate = factoryResult.RequestDelegate; + + await requestDelegate(httpContext); + + Assert.Equal(200, httpContext.Response.StatusCode); + Assert.False(httpContext.RequestAborted.IsCancellationRequested); + var decodedResponseBody = Encoding.UTF8.GetString(responseBodyStream.ToArray()); + Assert.Equal(expectedResponse, decodedResponseBody); + } + [Theory] [InlineData(true, "Age: 42")] [InlineData(false, "Age: ")] From c0f5a58a373a003e0733ba8b231a36d06b1eba5e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:50:25 +0000 Subject: [PATCH 3/3] Move struct default value tests to RequestDelegateCreationTests.QueryParameters.cs Agent-Logs-Url: https://github.com/dotnet/aspnetcore/sessions/ae7b0e98-8ab8-436c-9727-b09547e4abba Co-authored-by: BrennanConroy <7574801+BrennanConroy@users.noreply.github.com> --- .../test/RequestDelegateFactoryTests.cs | 60 ------------------- ...stDelegateCreationTests.QueryParameters.cs | 46 ++++++++++++++ 2 files changed, 46 insertions(+), 60 deletions(-) diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 6d17786aa43f..61e7d2a46965 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -1849,66 +1849,6 @@ public async Task CanSetParseableStringParamAsOptionalWithNullabilityDisability( Assert.Equal(expectedResponse, decodedResponseBody); } - [Theory] - [InlineData(true, "Id: a0e1f2b3-c4d5-4e6f-7a8b-9c0d1e2f3a4b")] - [InlineData(false, "Id: 00000000-0000-0000-0000-000000000000")] - public async Task CanSetGuidParamAsOptionalWithDefaultValue(bool provideValue, string expectedResponse) - { - string optionalQueryParam(Guid id = default) => $"Id: {id}"; - - var httpContext = CreateHttpContext(); - var responseBodyStream = new MemoryStream(); - httpContext.Response.Body = responseBodyStream; - - if (provideValue) - { - httpContext.Request.Query = new QueryCollection(new Dictionary - { - ["id"] = "a0e1f2b3-c4d5-4e6f-7a8b-9c0d1e2f3a4b" - }); - } - - var factoryResult = RequestDelegateFactory.Create(optionalQueryParam); - var requestDelegate = factoryResult.RequestDelegate; - - await requestDelegate(httpContext); - - Assert.Equal(200, httpContext.Response.StatusCode); - Assert.False(httpContext.RequestAborted.IsCancellationRequested); - var decodedResponseBody = Encoding.UTF8.GetString(responseBodyStream.ToArray()); - Assert.Equal(expectedResponse, decodedResponseBody); - } - - [Theory] - [InlineData(true, "Date: 2024-01-15T00:00:00.0000000")] - [InlineData(false, "Date: 0001-01-01T00:00:00.0000000")] - public async Task CanSetDateTimeParamAsOptionalWithDefaultValue(bool provideValue, string expectedResponse) - { - string optionalQueryParam(DateTime date = default) => $"Date: {date:O}"; - - var httpContext = CreateHttpContext(); - var responseBodyStream = new MemoryStream(); - httpContext.Response.Body = responseBodyStream; - - if (provideValue) - { - httpContext.Request.Query = new QueryCollection(new Dictionary - { - ["date"] = "2024-01-15" - }); - } - - var factoryResult = RequestDelegateFactory.Create(optionalQueryParam); - var requestDelegate = factoryResult.RequestDelegate; - - await requestDelegate(httpContext); - - Assert.Equal(200, httpContext.Response.StatusCode); - Assert.False(httpContext.RequestAborted.IsCancellationRequested); - var decodedResponseBody = Encoding.UTF8.GetString(responseBodyStream.ToArray()); - Assert.Equal(expectedResponse, decodedResponseBody); - } - [Theory] [InlineData(true, "Age: 42")] [InlineData(false, "Age: ")] diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.QueryParameters.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.QueryParameters.cs index 41dde7279e17..414dee57e5b1 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.QueryParameters.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.QueryParameters.cs @@ -139,6 +139,52 @@ public async Task MapAction_ExplicitParsableParameter_StringReturn() await VerifyResponseBodyAsync(httpContext, "10"); } + [Theory] + [InlineData(null, "Id: 00000000-0000-0000-0000-000000000000")] + [InlineData("a0e1f2b3-c4d5-4e6f-7a8b-9c0d1e2f3a4b", "Id: a0e1f2b3-c4d5-4e6f-7a8b-9c0d1e2f3a4b")] + public async Task CanSetGuidParamAsOptionalWithDefaultValue(string queryValue, string expectedResponse) + { + var (_, compilation) = await RunGeneratorAsync(""" +app.MapGet("/", ([FromQuery] Guid id = default) => $"Id: {id}"); +"""); + var endpoint = GetEndpointFromCompilation(compilation); + + var httpContext = CreateHttpContext(); + if (queryValue is not null) + { + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["id"] = queryValue + }); + } + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, expectedResponse); + } + + [Theory] + [InlineData(null, "Date: 0001-01-01T00:00:00.0000000")] + [InlineData("2024-01-15", "Date: 2024-01-15T00:00:00.0000000")] + public async Task CanSetDateTimeParamAsOptionalWithDefaultValue(string queryValue, string expectedResponse) + { + var (_, compilation) = await RunGeneratorAsync(""" +app.MapGet("/", ([FromQuery] DateTime date = default) => $"Date: {date:O}"); +"""); + var endpoint = GetEndpointFromCompilation(compilation); + + var httpContext = CreateHttpContext(); + if (queryValue is not null) + { + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["date"] = queryValue + }); + } + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, expectedResponse); + } + public static object[][] MapAction_ExplicitQueryParam_NameTest_Data { get