Version
WolverineFx.Http / WolverineFx.EntityFrameworkCore 6.17.1, .NET 10.
What happens
Combining [AsParameters] on the handler with a compound handler LoadAsync that also binds a route variable makes the runtime codegen emit code that does not compile, so the host fails at startup with InvalidOperationException: Compilation failures!:
CS0841: Cannot use local variable 'orderDbContext' before it is declared (x5)
CS0136: A local or parameter named 'order_id_rawValue' cannot be declared in this scope because that name is used in an enclosing local scope
CS0136: A local or parameter named 'order_id' cannot be declared in this scope because that name is used in an enclosing local scope
CS0136: A local or parameter named 'stockCardOrder' cannot be declared in this scope because that name is used in an enclosing local scope
The route variable is bound twice — once for the LoadAsync scope, once for the [AsParameters] binding frame — and the two sets of locals collide.
Repro
public sealed record UpsertLinesRequest
{
[FromRoute(Name = "order-id")]
public long OrderId { get; set; }
[FromBody]
public LinesBody Body { get; set; } = new([]);
}
public sealed record LinesBody(IReadOnlyList<string> Lines);
public static class UpsertLinesEndpoint
{
// compound handler: loads the aggregate from the same route variable
public static async Task<Order> LoadAsync(
[FromRoute(Name = "order-id")] long orderId,
OrderDbContext dbContext,
CancellationToken cancellationToken)
=> await dbContext.Orders.FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken)
?? throw new OrderNotFoundException();
[WolverinePut("/v3/orders/{order-id:long}/order-lines")]
public static async Task<Response> Put(
[AsParameters] UpsertLinesRequest request,
[NotBody] Order order,
[NotBody] OrderDbContext dbContext,
CancellationToken cancellationToken)
{
// ... uses request.Body.Lines and the loaded `order`
}
}
Variant also tried: making LoadAsync take the same [AsParameters] UpsertLinesRequest instead of the raw route parameter. Same failure, plus CS0136: A local or parameter named 'request' ... — the [AsParameters] binding frame is emitted in both scopes.
Works
Binding the route variable directly on the handler (and separately on LoadAsync) compiles and runs fine:
public static async Task<Response> Put(
[FromRoute(Name = "order-id")] long orderId, // unused in the body, aggregate still comes from LoadAsync
UpsertLinesRequest request,
Order order,
OrderDbContext dbContext,
CancellationToken cancellationToken)
Notes
This looks like the EF Core / compound-handler analogue of GH-3135, which was fixed for Marten's [WriteAggregate] (write_aggregate_with_asparameters): there, the route id flows through an [AsParameters] object while the aggregate is resolved from that same id. The same combination with a LoadAsync compound handler still breaks, at codegen time rather than with a 500.
Nothing in the AsParameters docs rules the combination out — the only documented restriction is form + body together.
Happy to send a PR if you point me at the right seam (the duplicate route-binding locals look like they come from the binding frames being emitted once per scope rather than reused).
Side note (possibly a separate issue — tell me and I'll open one)
Route variables consumed only by LoadAsync are missing from the generated OpenAPI operation. An endpoint whose route is /v3/orders/{orderId:long}/order-lines/{orderLineId:long}, where both ids are bound in LoadAsync and neither appears in the handler signature, produces an operation with "parameters": [] — the path parameters are not declared at all. It looks like the OpenAPI metadata is derived from the handler signature rather than from the route template plus the full binding chain.
Version
WolverineFx.Http / WolverineFx.EntityFrameworkCore 6.17.1, .NET 10.
What happens
Combining
[AsParameters]on the handler with a compound handlerLoadAsyncthat also binds a route variable makes the runtime codegen emit code that does not compile, so the host fails at startup withInvalidOperationException: Compilation failures!:The route variable is bound twice — once for the
LoadAsyncscope, once for the[AsParameters]binding frame — and the two sets of locals collide.Repro
Variant also tried: making
LoadAsynctake the same[AsParameters] UpsertLinesRequestinstead of the raw route parameter. Same failure, plusCS0136: A local or parameter named 'request' ...— the[AsParameters]binding frame is emitted in both scopes.Works
Binding the route variable directly on the handler (and separately on
LoadAsync) compiles and runs fine:Notes
This looks like the EF Core / compound-handler analogue of GH-3135, which was fixed for Marten's
[WriteAggregate](write_aggregate_with_asparameters): there, the route id flows through an[AsParameters]object while the aggregate is resolved from that same id. The same combination with aLoadAsynccompound handler still breaks, at codegen time rather than with a 500.Nothing in the
AsParametersdocs rules the combination out — the only documented restriction is form + body together.Happy to send a PR if you point me at the right seam (the duplicate route-binding locals look like they come from the binding frames being emitted once per scope rather than reused).
Side note (possibly a separate issue — tell me and I'll open one)
Route variables consumed only by
LoadAsyncare missing from the generated OpenAPI operation. An endpoint whose route is/v3/orders/{orderId:long}/order-lines/{orderLineId:long}, where both ids are bound inLoadAsyncand neither appears in the handler signature, produces an operation with"parameters": []— the path parameters are not declared at all. It looks like the OpenAPI metadata is derived from the handler signature rather than from the route template plus the full binding chain.