When working with .NET minimal APIs, the endpoint definition may be improved by utilizing TypedResults for higher OpenAPI documentation. Nevertheless, this seemingly easy change can result in complicated compiler errors, similar to “Delegate RequestDelegate doesn’t take X arguments.” Even the GitHub Copilot coding agent struggled to resolve these points routinely.
On this article, we’ll discover why this error happens, what it means in your API design, and easy methods to resolve it utilizing the generic Outcomes kind.
Copilot coding agent outcomes
After GitHub Copilot coding agent created an API for me, with a pull request evaluate I requested to:
- With the minimal APIs within the
EduValidate.Apiservice
mission, create an endpoints-class and transfer the minimal APIs code fromProgram.cs
to this file. - With minimal APIs, use
TypedResults
as an alternative ofOutcomes
Copilot succeeded doing the primary process, however failed with the second:
✅ Created endpoints class – Moved minimal API code from Program.cs to Endpoints/CodeAnalysisEndpoints.cs
⚠️ TypedResults situation – Found that changing Outcomes with TypedResults causes compilation errors (“Delegate RequestDelegate doesn’t take X arguments”). This seems to be a kind inference situation within the minimal API processing. The endpoints work accurately with Outcomes for now.
I had some extra duties to repair, and Copilot fastened all of them except this one. Many builders encounter this error. Let’s look at why it occurs and easy methods to resolve it.
Utilizing Outcomes
A minimal APIs code utilizing the Outcomes
class to return outcomes can appear like this:
app.MapGet("/api/submission/{submissionId}/evaluation", async (
string submissionId,
EduValidateDbContext dbContext,
CancellationToken cancellationToken) =>
{
// some code
if (submission == null)
{
return Outcomes.NotFound($"Submission {submissionId} not discovered");
}
// some code
return Outcomes.Okay(analysisResult);
})
.WithName("GetAnalysis")
.WithSummary("Get evaluation outcomes for a submission");
In case there’s simply just one doable end result kind, the Outcomes
class can simply get replaced with a TypedResults
, however with two or extra occurrences, this code now not compiles:
app.MapGet("/api/submission/{submissionId}/evaluation", async (
string submissionId,
EduValidateDbContext dbContext,
CancellationToken cancellationToken) =>
{
// some code
if (submission == null)
{
return TypedResults.NotFound($"Submission {submissionId} not discovered");
}
// some code
return TypedResults.Okay(analysisResult);
})
.WithName("GetAnalysis")
.WithSummary("Get evaluation outcomes for a submission");
The compiler complains with Delegate RequestDelegate
doesn’t take 3 arguments.
Why utilizing TypedResults
in any respect? When utilizing TypedResults
, the knowledge what may be returned from the API is added to the OpenAPI doc, with out the necessity to add attributes for each end result kind.
What’s the rationale for this error?
Why does the RequestDelegate
overload match?
The strategy MapGet
is specified with two overloads.
One is the one we want to use when passing an expression. This methodology has been added for minimal APIs:
[RequiresDynamicCode("This API may perform reflection on the supplied delegate and its parameters. These types may require generated code and aren't compatible with native AOT applications.")]
[RequiresUnreferencedCode("This API may perform reflection on the supplied delegate and its parameters. These types may be trimmed if not directly referenced.")]
public static RouteHandlerBuilder MapGet(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string sample, Delegate handler);
This overload existed earlier than minimal APIs have been accessible. This methodology receives a RequestDelegate
parameter:
public static IEndpointConventionBuilder MapGet(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string sample, RequestDelegate requestDelegate);
The compiler now assumes to make use of this overload as an alternative of the earlier one. Nevertheless, this overload wants a match with parameters and the return kind for the RequestDelegate
delegate kind. This definition returns a Job
, and may solely use a HttpContext
as parameter.
public delegate Job RequestDelegate(HttpContext context);
When enabling to make use of the RequestDelegateGenerator (which is used with native AOT, or specifying
EnableRequestDelegateGenerator
within the mission file), the supply generator replaces the tactic with theDelegate
parameter to make use of the tactic with theRequestDelegate
parameter as an alternative, which permits minimal APIs with native AOT.
Utilizing a lambda expression, the compiler checks for all ´returnstatements throughout the methodology to resolve the end result kind. The strategies of the
Outcomesclass return
IResult. Nevertheless,
TypedResults.Okay<T>returns
Okay<T>, and
TypedResults.NotFound<string>returns
NotFound<string>. Because the compiler can not resolve the end result kind immediately, the
RequestDelegate` overload is used, which doesn’t match the variety of parameters.
All these return sorts implement the interface IResult
. Thus altering the return kind to Job<IResult>
helps compiling:
app.MapGet("/api/submission/{submissionId}/evaluation", async Job<IResult> (
string submissionId,
EduValidateDbContext dbContext,
CancellationToken cancellationToken) =>
{
// some code
if (submission == null)
{
return TypedResults.NotFound($"Submission {submissionId} not discovered");
}
// some code
return TypedResults.Okay(analysisResult);
})
.WithName("GetAnalysis")
.WithSummary("Get evaluation outcomes for a submission");
Nevertheless, this isn’t the repair wanted so as to add the end result data to the OpenAPI description.
The repair
This may be fastened by utilizing the generic Outcomes
kind which permits utilizing 2, 3, 4, or extra generic parameters sorts, and specifying the end result sorts:
app.MapGet("/api/submission/{submissionId}/evaluation", async Job<Outcomes<Okay<CodeAnalysisResult>, NotFound<string>>> (
string submissionId,
EduValidateDbContext dbContext,
ILogger<ICodeAnalysisService> logger,
CancellationToken cancellationToken) =>
{
The outcomes returned is both Okay<CodeAnalysisResult>
, or NotFound<string>
, wrapped right into a Job
.
With this alteration, the knowledge on the return values goes to the OpenAPI description.
The Copilot coding agent is enhanced constantly. It’s additionally doable to attach him to be taught from my very own repositories, the place I’m certain he would have resolved the difficulty routinely. Perhaps he additionally learns from studying this text 🙂
Abstract
In abstract, when constructing minimal APIs, utilizing the generic Outcomes kind with particular end result sorts not solely resolves complicated compiler errors but in addition improves your OpenAPI documentation. This strategy ensures your endpoints are each sturdy and well-documented.
For extra data:
Creating APIs is roofed in Chapter 2 of my ebook: