Skip to content

Commit 46a3d2b

Browse files
Add BindAsync support to RequestDelegateGenerator (#47033)
* Add BindAsync support to RequestDelegateGenerator # Conflicts: # src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs # src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointParameter.cs # src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt # src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt # src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt # src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleTimeOnlyParam_StringReturn.generated.txt # src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt # src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTests.cs * Support explicit interface implementations * Add MapAction_BindAsync_Snapshot * Route parameters do not support BindAsync * Move test types to shared types --------- Co-authored-by: Safia Abdalla <[email protected]>
1 parent 02df4ac commit 46a3d2b

File tree

36 files changed

+2767
-475
lines changed

36 files changed

+2767
-475
lines changed

src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,10 @@ internal static class DiagnosticDescriptors
161161
isEnabledByDefault: true,
162162
helpLinkUri: "https://aka.ms/aspnet/analyzers");
163163

164-
internal static readonly DiagnosticDescriptor RouteParameterComplexTypeIsNotParsableOrBindable = new(
164+
internal static readonly DiagnosticDescriptor RouteParameterComplexTypeIsNotParsable = new(
165165
"ASP0020",
166166
"Complex types referenced by route parameters must be parsable",
167-
"Parameter '{0}' of type {1} should define a bool TryParse(string, IFormatProvider, out {1}) method, or implement IParsable<{1}>, or define a ValueTask<{1}> BindAsync(HttpContext), or implement IBindableFromHttpContext<{1}>.",
167+
"Parameter '{0}' of type {1} should define a bool TryParse(string, IFormatProvider, out {1}) method, or implement IParsable<{1}>.",
168168
"Usage",
169169
DiagnosticSeverity.Error,
170170
isEnabledByDefault: true,

src/Framework/AspNetCoreAnalyzers/src/Analyzers/Microsoft.AspNetCore.App.Analyzers.csproj

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@
2727
<Compile Include="$(SharedSourceRoot)RoslynUtils\WellKnownTypes.cs" LinkBase="Shared" />
2828
<Compile Include="$(SharedSourceRoot)RoslynUtils\WellKnownTypeData.cs" LinkBase="Shared" />
2929
<Compile Include="$(SharedSourceRoot)RoslynUtils\SymbolExtensions.cs" LinkBase="Shared" />
30-
<Compile Include="$(SharedSourceRoot)RoslynUtils\Bindability.cs" LinkBase="Shared"/>
31-
<Compile Include="$(SharedSourceRoot)RoslynUtils\Parsability.cs" LinkBase="Shared"/>
32-
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityMethod.cs" LinkBase="Shared"/>
33-
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityHelper.cs" LinkBase="Shared"/>
30+
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityHelper.cs" LinkBase="Shared" />
3431
</ItemGroup>
3532

3633
</Project>

src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlers/DisallowNonParsableComplexTypesOnParameters.cs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,11 @@ private static void DisallowNonParsableComplexTypesOnParameters(
6868
if (IsRouteParameter(routeUsage, handlerDelegateParameter))
6969
{
7070
var parsability = ParsabilityHelper.GetParsability(parameterTypeSymbol, wellKnownTypes);
71-
var bindability = ParsabilityHelper.GetBindability(parameterTypeSymbol, wellKnownTypes);
7271

73-
if (!(parsability == Parsability.Parsable || bindability == Bindability.Bindable))
72+
if (parsability != Parsability.Parsable)
7473
{
75-
var descriptor = SelectDescriptor(parsability, bindability);
76-
7774
context.ReportDiagnostic(Diagnostic.Create(
78-
descriptor,
75+
DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsable,
7976
location,
8077
handlerDelegateParameter.Name,
8178
parameterTypeSymbol.Name
@@ -103,10 +100,8 @@ static bool ReportFromAttributeDiagnostic(OperationAnalysisContext context, Well
103100
var parsability = ParsabilityHelper.GetParsability(parameterTypeSymbol, wellKnownTypes);
104101
if (parameter.HasAttributeImplementingInterface(fromMetadataInterfaceTypeSymbol) && parsability != Parsability.Parsable)
105102
{
106-
var descriptor = SelectDescriptor(parsability, Bindability.NotBindable);
107-
108103
context.ReportDiagnostic(Diagnostic.Create(
109-
descriptor,
104+
DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsable,
110105
location,
111106
parameter.Name,
112107
parameterTypeSymbol.Name
@@ -140,16 +135,5 @@ static bool ReportFromAttributeDiagnostic(OperationAnalysisContext context, Well
140135

141136
return parameterTypeSymbol;
142137
}
143-
144-
static DiagnosticDescriptor SelectDescriptor(Parsability parsability, Bindability bindability)
145-
{
146-
// This abomination is used to take the parsability and bindability and together figure
147-
// out what the most optimal diagnostic message is to give to our plucky user.
148-
return (parsability, bindability) switch
149-
{
150-
{ parsability: Parsability.NotParsable, bindability: Bindability.InvalidReturnType } => DiagnosticDescriptors.BindAsyncSignatureMustReturnValueTaskOfT,
151-
_ => DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsableOrBindable
152-
};
153-
}
154138
}
155139
}

src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlers/RouteHandlerAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public partial class RouteHandlerAnalyzer : DiagnosticAnalyzer
2525
DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers,
2626
DiagnosticDescriptors.DetectMisplacedLambdaAttribute,
2727
DiagnosticDescriptors.DetectMismatchedParameterOptionality,
28-
DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsableOrBindable,
28+
DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsable,
2929
DiagnosticDescriptors.BindAsyncSignatureMustReturnValueTaskOfT,
3030
DiagnosticDescriptors.AmbiguousRouteHandlerRoute,
3131
DiagnosticDescriptors.AtMostOneFromBodyAttribute

src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DisallowNonParsableComplexTypesOnParametersTest.cs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public class Customer
195195
}
196196
""";
197197

198-
var expectedDiagnostic = new DiagnosticResult(DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsableOrBindable)
198+
var expectedDiagnostic = new DiagnosticResult(DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsable)
199199
.WithArguments("customer", "Customer")
200200
.WithLocation(0);
201201

@@ -218,7 +218,7 @@ public class Customer
218218
}
219219
""";
220220

221-
var expectedDiagnostic = new DiagnosticResult(DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsableOrBindable)
221+
var expectedDiagnostic = new DiagnosticResult(DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsable)
222222
.WithArguments("customer", "Customer")
223223
.WithLocation(0);
224224

@@ -289,7 +289,7 @@ public static bool TryParse(string s, IFormatProvider provider, out Customer res
289289
}
290290

291291
[Fact]
292-
public async Task Route_Parameter_withBindAsyncMethodThatReturnsTask_of_T_Fails()
292+
public async Task Route_Parameter_withBindAsyncMethod_Fails()
293293
{
294294
// Arrange
295295
var source = $$"""
@@ -301,14 +301,14 @@ public async Task Route_Parameter_withBindAsyncMethodThatReturnsTask_of_T_Fails(
301301
302302
public class Customer
303303
{
304-
public async static Task<Customer> BindAsync(HttpContext context)
304+
public async static ValueTask<Customer> BindAsync(HttpContext context)
305305
{
306306
return new Customer();
307307
}
308308
}
309309
""";
310310

311-
var expectedDiagnostic = new DiagnosticResult(DiagnosticDescriptors.BindAsyncSignatureMustReturnValueTaskOfT)
311+
var expectedDiagnostic = new DiagnosticResult(DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsable)
312312
.WithArguments("customer", "Customer")
313313
.WithLocation(0);
314314

@@ -422,7 +422,7 @@ public static bool TryParse(string s, IFormatProvider provider, out Customer res
422422
}
423423

424424
[Fact]
425-
public async Task Route_Parameter_withHttpContextBindableComplexType_viaImplicitIBindableFromHttp_Works()
425+
public async Task Route_Parameter_withHttpContextBindableComplexType_viaImplicitIBindableFromHttp_Fails()
426426
{
427427
// Arrange
428428
var source = $$"""
@@ -433,7 +433,7 @@ public async Task Route_Parameter_withHttpContextBindableComplexType_viaImplicit
433433
using Microsoft.AspNetCore.Http;
434434
435435
var webApp = WebApplication.Create();
436-
webApp.MapGet("/customers/{customer}", (Customer customer) => {});
436+
webApp.MapGet("/customers/{customer}", ({|#0:Customer customer|}) => {});
437437
438438
public class Customer : IBindableFromHttpContext<Customer>
439439
{
@@ -444,12 +444,16 @@ public class Customer : IBindableFromHttpContext<Customer>
444444
}
445445
""";
446446

447+
var expectedDiagnostic = new DiagnosticResult(DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsable)
448+
.WithArguments("customer", "Customer")
449+
.WithLocation(0);
450+
447451
// Act
448-
await VerifyCS.VerifyAnalyzerAsync(source);
452+
await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostic);
449453
}
450454

451455
[Fact]
452-
public async Task Route_Parameter_withHttpContextBindableComplexType_viaExplicitIBindableFromHttp_Works()
456+
public async Task Route_Parameter_withHttpContextBindableComplexType_viaExplicitIBindableFromHttp_Fails()
453457
{
454458
// Arrange
455459
var source = $$"""
@@ -460,7 +464,7 @@ public async Task Route_Parameter_withHttpContextBindableComplexType_viaExplicit
460464
using Microsoft.AspNetCore.Http;
461465
462466
var webApp = WebApplication.Create();
463-
webApp.MapGet("/customers/{customer}", (Customer customer) => {});
467+
webApp.MapGet("/customers/{customer}", ({|#0:Customer customer|}) => {});
464468
465469
public class Customer : IBindableFromHttpContext<Customer>
466470
{
@@ -471,8 +475,12 @@ public class Customer : IBindableFromHttpContext<Customer>
471475
}
472476
""";
473477

478+
var expectedDiagnostic = new DiagnosticResult(DiagnosticDescriptors.RouteParameterComplexTypeIsNotParsable)
479+
.WithArguments("customer", "Customer")
480+
.WithLocation(0);
481+
474482
// Act
475-
await VerifyCS.VerifyAnalyzerAsync(source);
483+
await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostic);
476484
}
477485

478486
[Fact]

src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.Generators.csproj

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@
2424
<Compile Include="$(SharedSourceRoot)RoslynUtils\WellKnownTypeData.cs" LinkBase="Shared" />
2525
<Compile Include="$(SharedSourceRoot)RoslynUtils\WellKnownTypes.cs" LinkBase="Shared" />
2626
<Compile Include="$(SharedSourceRoot)RoslynUtils\SymbolExtensions.cs" LinkBase="Shared" />
27-
<Compile Include="$(SharedSourceRoot)RoslynUtils\Bindability.cs" LinkBase="Shared"/>
28-
<Compile Include="$(SharedSourceRoot)RoslynUtils\Parsability.cs" LinkBase="Shared"/>
29-
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityMethod.cs" LinkBase="Shared"/>
30-
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityHelper.cs" LinkBase="Shared"/>
27+
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityHelper.cs" LinkBase="Shared" />
3128
<Compile Include="$(SharedSourceRoot)RoslynUtils\CodeWriter.cs" LinkBase="Shared" />
3229
</ItemGroup>
3330

src/Http/Http.Extensions/gen/RequestDelegateGenerator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
8181
codeWriter.WriteLine("EndpointFilterDelegate? filteredInvocation = null;");
8282
endpoint.EmitRouteOrQueryResolver(codeWriter);
8383
endpoint.EmitJsonPreparation(codeWriter);
84+
if (endpoint.NeedsParameterArray)
85+
{
86+
codeWriter.WriteLine("var parameters = del.Method.GetParameters();");
87+
}
8488
codeWriter.WriteLineNoTabs(string.Empty);
8589
codeWriter.WriteLine("if (options?.EndpointBuilder?.FilterFactories.Count > 0)");
8690
codeWriter.StartBlock();

src/Http/Http.Extensions/gen/RequestDelegateGeneratorSources.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ private static Task WriteToResponseAsync<T>(T? value, HttpContext httpContext, J
158158
}
159159
return (false, default);
160160
}
161+
162+
private static ValueTask<T?> BindAsync<T>(HttpContext context, ParameterInfo parameter)
163+
where T : class, IBindableFromHttpContext<T>
164+
{
165+
return T.BindAsync(context, parameter);
166+
}
161167
}
162168
}
163169
""";

src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointEmitter.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using System.Linq;
64
using System.Globalization;
75
using System.IO;
8-
using System.Text;
6+
using System.Linq;
97

108
namespace Microsoft.AspNetCore.Http.Generators.StaticRouteHandlerModel.Emitters;
119
internal static class EndpointEmitter
@@ -17,24 +15,28 @@ internal static string EmitParameterPreparation(this Endpoint endpoint, int base
1715

1816
foreach (var parameter in endpoint.Parameters)
1917
{
20-
switch (parameter)
18+
switch (parameter.Source)
2119
{
22-
case { Source: EndpointParameterSource.SpecialType }:
20+
case EndpointParameterSource.SpecialType:
2321
parameter.EmitSpecialParameterPreparation(parameterPreparationBuilder);
2422
break;
25-
case { Source: EndpointParameterSource.Query or EndpointParameterSource.Header }:
23+
case EndpointParameterSource.Query:
24+
case EndpointParameterSource.Header:
2625
parameter.EmitQueryOrHeaderParameterPreparation(parameterPreparationBuilder);
2726
break;
28-
case { Source: EndpointParameterSource.Route }:
27+
case EndpointParameterSource.Route:
2928
parameter.EmitRouteParameterPreparation(parameterPreparationBuilder);
3029
break;
31-
case { Source: EndpointParameterSource.RouteOrQuery }:
30+
case EndpointParameterSource.RouteOrQuery:
3231
parameter.EmitRouteOrQueryParameterPreparation(parameterPreparationBuilder);
3332
break;
34-
case { Source: EndpointParameterSource.JsonBody }:
33+
case EndpointParameterSource.BindAsync:
34+
parameter.EmitBindAsyncPreparation(parameterPreparationBuilder);
35+
break;
36+
case EndpointParameterSource.JsonBody:
3537
parameter.EmitJsonBodyParameterPreparationString(parameterPreparationBuilder);
3638
break;
37-
case { Source: EndpointParameterSource.Service }:
39+
case EndpointParameterSource.Service:
3840
parameter.EmitServiceParameterPreparation(parameterPreparationBuilder);
3941
break;
4042
}

src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Text;
5+
using Microsoft.AspNetCore.Analyzers.Infrastructure;
6+
using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
67
using Microsoft.CodeAnalysis;
78

89
namespace Microsoft.AspNetCore.Http.Generators.StaticRouteHandlerModel.Emitters;
10+
911
internal static class EndpointParameterEmitter
1012
{
1113
internal static void EmitSpecialParameterPreparation(this EndpointParameter endpointParameter, CodeWriter codeWriter)
@@ -26,15 +28,15 @@ internal static void EmitQueryOrHeaderParameterPreparation(this EndpointParamete
2628
// compiler errors around null handling.
2729
if (endpointParameter.IsOptional)
2830
{
29-
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = {endpointParameter.EmitAssigningCodeResult()}.Count > 0 ? {endpointParameter.EmitAssigningCodeResult()}.ToString() : null;");
31+
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = {endpointParameter.EmitAssigningCodeResult()}.Count > 0 ? (string?){endpointParameter.EmitAssigningCodeResult()} : null;");
3032
}
3133
else
3234
{
3335
codeWriter.WriteLine($"if (StringValues.IsNullOrEmpty({endpointParameter.EmitAssigningCodeResult()}))");
3436
codeWriter.StartBlock();
3537
codeWriter.WriteLine("wasParamCheckFailure = true;");
3638
codeWriter.EndBlock();
37-
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = {endpointParameter.EmitAssigningCodeResult()}.ToString();");
39+
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = (string?){endpointParameter.EmitAssigningCodeResult()};");
3840
}
3941

4042
endpointParameter.EmitParsingBlock(codeWriter);
@@ -64,7 +66,7 @@ internal static void EmitRouteParameterPreparation(this EndpointParameter endpoi
6466
codeWriter.WriteLine($$"""throw new InvalidOperationException($"'{{endpointParameter.Name}}' is not a route parameter.");""");
6567
codeWriter.EndBlock();
6668

67-
var assigningCode = $"httpContext.Request.RouteValues[\"{endpointParameter.Name}\"]?.ToString()";
69+
var assigningCode = $"(string?)httpContext.Request.RouteValues[\"{endpointParameter.Name}\"]";
6870
codeWriter.WriteLine($"var {endpointParameter.EmitAssigningCodeResult()} = {assigningCode};");
6971

7072
if (!endpointParameter.IsOptional)
@@ -75,7 +77,7 @@ internal static void EmitRouteParameterPreparation(this EndpointParameter endpoi
7577
codeWriter.EndBlock();
7678
}
7779

78-
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = {endpointParameter.EmitAssigningCodeResult()}?.ToString();");
80+
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = (string?){endpointParameter.EmitAssigningCodeResult()};");
7981
endpointParameter.EmitParsingBlock(codeWriter);
8082
}
8183

@@ -94,7 +96,8 @@ internal static void EmitRouteOrQueryParameterPreparation(this EndpointParameter
9496
codeWriter.EndBlock();
9597
}
9698

97-
codeWriter.WriteLine($"var {endpointParameter.EmitHandlerArgument()} = {endpointParameter.EmitAssigningCodeResult()};");
99+
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = (string?){endpointParameter.EmitAssigningCodeResult()};");
100+
endpointParameter.EmitParsingBlock(codeWriter);
98101
}
99102

100103
internal static void EmitJsonBodyParameterPreparationString(this EndpointParameter endpointParameter, CodeWriter codeWriter)
@@ -116,6 +119,47 @@ internal static void EmitJsonBodyParameterPreparationString(this EndpointParamet
116119
codeWriter.EndBlock();
117120
}
118121

122+
internal static void EmitBindAsyncPreparation(this EndpointParameter endpointParameter, CodeWriter codeWriter)
123+
{
124+
var unwrappedType = endpointParameter.Type.UnwrapTypeSymbol();
125+
var unwrappedTypeString = unwrappedType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
126+
127+
switch (endpointParameter.BindMethod)
128+
{
129+
case BindabilityMethod.IBindableFromHttpContext:
130+
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await BindAsync<{unwrappedTypeString}>(httpContext, parameters[{endpointParameter.Ordinal}]);");
131+
break;
132+
case BindabilityMethod.BindAsyncWithParameter:
133+
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await {unwrappedTypeString}.BindAsync(httpContext, parameters[{endpointParameter.Ordinal}]);");
134+
break;
135+
case BindabilityMethod.BindAsync:
136+
codeWriter.WriteLine($"var {endpointParameter.EmitTempArgument()} = await {unwrappedTypeString}.BindAsync(httpContext);");
137+
break;
138+
default:
139+
throw new Exception("Unreachable!");
140+
}
141+
142+
// TODO: Generate more compact code if the type is a reference type and/or the BindAsync return nullability matches the handler parameter type.
143+
if (endpointParameter.IsOptional)
144+
{
145+
codeWriter.WriteLine($"var {endpointParameter.EmitHandlerArgument()} = ({unwrappedTypeString}?){endpointParameter.EmitTempArgument()};");
146+
}
147+
else
148+
{
149+
codeWriter.WriteLine($"{unwrappedTypeString} {endpointParameter.EmitHandlerArgument()};");
150+
151+
codeWriter.WriteLine($"if ((object?){endpointParameter.EmitTempArgument()} == null)");
152+
codeWriter.StartBlock();
153+
codeWriter.WriteLine("wasParamCheckFailure = true;");
154+
codeWriter.WriteLine($"{endpointParameter.EmitHandlerArgument()} = default!;");
155+
codeWriter.EndBlock();
156+
codeWriter.WriteLine("else");
157+
codeWriter.StartBlock();
158+
codeWriter.WriteLine($"{endpointParameter.EmitHandlerArgument()} = ({unwrappedTypeString}){endpointParameter.EmitTempArgument()};");
159+
codeWriter.EndBlock();
160+
}
161+
}
162+
119163
internal static void EmitServiceParameterPreparation(this EndpointParameter endpointParameter, CodeWriter codeWriter)
120164
{
121165
codeWriter.WriteLine(endpointParameter.EmitParameterDiagnosticComment());

0 commit comments

Comments
 (0)