diff --git a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs index e68aeb559e94..0aafd008c5d1 100644 --- a/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs +++ b/src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs @@ -389,8 +389,8 @@ internal static void EmitServiceParameterPreparation(this EndpointParameter endp // Unlike other scenarios, this will result in an exception being thrown // at runtime. var assigningCode = endpointParameter.IsOptional ? - $"httpContext.RequestServices.GetService<{endpointParameter.Type}>();" : - $"httpContext.RequestServices.GetRequiredService<{endpointParameter.Type}>()"; + $"httpContext.RequestServices.GetService<{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}>();" : + $"httpContext.RequestServices.GetRequiredService<{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}>()"; codeWriter.WriteLine($"var {endpointParameter.EmitHandlerArgument()} = {assigningCode};"); } @@ -404,8 +404,8 @@ internal static void EmitKeyedServiceParameterPreparation(this EndpointParameter codeWriter.EndBlock(); var assigningCode = endpointParameter.IsOptional ? - $"httpContext.RequestServices.GetKeyedService<{endpointParameter.Type}>({endpointParameter.KeyedServiceKey});" : - $"httpContext.RequestServices.GetRequiredKeyedService<{endpointParameter.Type}>({endpointParameter.KeyedServiceKey})"; + $"httpContext.RequestServices.GetKeyedService<{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}>({endpointParameter.KeyedServiceKey});" : + $"httpContext.RequestServices.GetRequiredKeyedService<{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}>({endpointParameter.KeyedServiceKey})"; codeWriter.WriteLine($"var {endpointParameter.EmitHandlerArgument()} = {assigningCode};"); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt index 0a2c48bde9b4..a82fa6fb9baf 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt @@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Http.Generated { var wasParamCheckFailure = false; // Endpoint Parameter: svc (Type = Microsoft.AspNetCore.Http.Generators.Tests.TestService, IsOptional = False, IsParsable = False, IsArray = False, Source = Service) - var svc_local = httpContext.RequestServices.GetRequiredService(); + var svc_local = httpContext.RequestServices.GetRequiredService(); if (wasParamCheckFailure) { @@ -130,7 +130,7 @@ namespace Microsoft.AspNetCore.Http.Generated { var wasParamCheckFailure = false; // Endpoint Parameter: svc (Type = Microsoft.AspNetCore.Http.Generators.Tests.TestService, IsOptional = False, IsParsable = False, IsArray = False, Source = Service) - var svc_local = httpContext.RequestServices.GetRequiredService(); + var svc_local = httpContext.RequestServices.GetRequiredService(); if (wasParamCheckFailure) { @@ -207,7 +207,7 @@ namespace Microsoft.AspNetCore.Http.Generated { var wasParamCheckFailure = false; // Endpoint Parameter: svc (Type = System.Collections.Generic.IEnumerable, IsOptional = False, IsParsable = False, IsArray = False, Source = Service) - var svc_local = httpContext.RequestServices.GetRequiredService>(); + var svc_local = httpContext.RequestServices.GetRequiredService>(); if (wasParamCheckFailure) { @@ -230,7 +230,7 @@ namespace Microsoft.AspNetCore.Http.Generated { var wasParamCheckFailure = false; // Endpoint Parameter: svc (Type = System.Collections.Generic.IEnumerable, IsOptional = False, IsParsable = False, IsArray = False, Source = Service) - var svc_local = httpContext.RequestServices.GetRequiredService>(); + var svc_local = httpContext.RequestServices.GetRequiredService>(); if (wasParamCheckFailure) { @@ -308,9 +308,9 @@ namespace Microsoft.AspNetCore.Http.Generated { var wasParamCheckFailure = false; // Endpoint Parameter: svc (Type = Microsoft.AspNetCore.Http.Generators.Tests.TestService?, IsOptional = True, IsParsable = False, IsArray = False, Source = Service) - var svc_local = httpContext.RequestServices.GetService();; + var svc_local = httpContext.RequestServices.GetService();; // Endpoint Parameter: svcs (Type = System.Collections.Generic.IEnumerable, IsOptional = False, IsParsable = False, IsArray = False, Source = Service) - var svcs_local = httpContext.RequestServices.GetRequiredService>(); + var svcs_local = httpContext.RequestServices.GetRequiredService>(); if (wasParamCheckFailure) { @@ -333,9 +333,9 @@ namespace Microsoft.AspNetCore.Http.Generated { var wasParamCheckFailure = false; // Endpoint Parameter: svc (Type = Microsoft.AspNetCore.Http.Generators.Tests.TestService?, IsOptional = True, IsParsable = False, IsArray = False, Source = Service) - var svc_local = httpContext.RequestServices.GetService();; + var svc_local = httpContext.RequestServices.GetService();; // Endpoint Parameter: svcs (Type = System.Collections.Generic.IEnumerable, IsOptional = False, IsParsable = False, IsArray = False, Source = Service) - var svcs_local = httpContext.RequestServices.GetRequiredService>(); + var svcs_local = httpContext.RequestServices.GetRequiredService>(); if (wasParamCheckFailure) { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTestBase.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTestBase.cs index aefa72875046..559e3b422524 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTestBase.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTestBase.cs @@ -287,6 +287,7 @@ internal static string GetMapActionString(string sources, string className = "Te using Microsoft.AspNetCore.Http.Generators.Tests; using Microsoft.Extensions.Primitives; using Microsoft.Extensions.DependencyInjection; +using Http; public static class {{className}} { diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.KeyServices.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.KeyServices.cs index c7dcb131c2c3..08e3f22e9b83 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.KeyServices.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.KeyServices.cs @@ -3,6 +3,7 @@ using System.Globalization; using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Http.RequestDelegateGenerator; +using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; @@ -225,6 +226,32 @@ public async Task ThrowsIfDiContainerDoesNotSupportKeyedServices() Assert.Equal("Unable to resolve service referenced by FromKeyedServicesAttribute. The service provider doesn't support keyed services.", exception.Message); } + // See: https://github.com/dotnet/aspnetcore/issues/58633 + [Fact] + public async Task RequestDelegateGeneratesCompilableCodeForKeyedServiceInNamespaceHttp() + { + var source = """ +app.MapGet("/hello", ([FromKeyedServices("example")] global::Http.ExampleService e) => e.Act("To be or not to be…")); +"""; + var (results, compilation) = await RunGeneratorAsync(source); + + // Ironically, the same error this is testing would bite us here, so we must globally qualify the type name. + var serviceProvider = CreateServiceProvider((serviceCollection) => serviceCollection.AddKeyedSingleton("example")); + var endpoint = GetEndpointFromCompilation(compilation, serviceProvider: serviceProvider); + + VerifyStaticEndpointModel(results, endpointModel => + { + Assert.Equal("MapGet", endpointModel.HttpMethod); + var p = Assert.Single(endpointModel.Parameters); + Assert.Equal(EndpointParameterSource.KeyedService, p.Source); + Assert.Equal("e", p.SymbolName); + }); + + var httpContext = CreateHttpContext(serviceProvider); + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "To be or not to be…"); + } + private class MockServiceProvider : IServiceProvider, ISupportRequiredService { public object GetService(Type serviceType) diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.cs index 8823f162b56b..9e4f803968e8 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.cs @@ -599,4 +599,30 @@ public async Task RequestDelegateHandlesStringValuesFromExplicitQueryStringSourc Assert.Equal(new[] { 4, 5, 6 }, (int[])httpContext.Items["headers"]!); Assert.Equal(new[] { 7, 8, 9 }, (int[])httpContext.Items["form"]!); } + + // See: https://github.com/dotnet/aspnetcore/issues/58633 + [Fact] + public async Task RequestDelegateGeneratesCompilableCodeForServiceInNamespaceHttp() + { + var source = """ +app.MapGet("/hello", ([FromServices] ExampleService e) => e.Act("To be or not to be…")); +"""; + var (results, compilation) = await RunGeneratorAsync(source); + + // Ironically, the same error this is testing would bite us here, so we must globally qualify the type name. + var serviceProvider = CreateServiceProvider((serviceCollection) => serviceCollection.AddSingleton()); + var endpoint = GetEndpointFromCompilation(compilation, serviceProvider: serviceProvider); + + VerifyStaticEndpointModel(results, endpointModel => + { + Assert.Equal("MapGet", endpointModel.HttpMethod); + var p = Assert.Single(endpointModel.Parameters); + Assert.Equal(EndpointParameterSource.Service, p.Source); + Assert.Equal("e", p.SymbolName); + }); + + var httpContext = CreateHttpContext(serviceProvider); + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, "To be or not to be…"); + } } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/SharedTypes.Http.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/SharedTypes.Http.cs new file mode 100644 index 000000000000..419a5a8034b9 --- /dev/null +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/SharedTypes.Http.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + +namespace Http; + +#nullable enable + +public sealed class ExampleService +{ + public string Act(string line) => line; +}