Introduce the Results<TResult1, TResult2, TResultN> union types#40986
Conversation
Contributes to #40672
- Add new API to PublicAPIUnshipped.txt - Suppress API baseline in code gen tool - Add code gen tool to the HttpAbstractsions.slnf file
- Added README.md explaining ResultsOfT code generator tool - Added comment to top of ResultsOfT.cs pointing to code generator tool - Updated Http\README.md to include Http.Results
Also added a sample
|
Is not possible (or make sense) use a T4 template instead of introduce a new project to generate the code? |
I think to date the repo has used console apps for code generation in numerous places (Kestrel, Developer Exception Page, etc.) so I followed that pattern. I'm not super fussed other than it would mean more work to convert what I've done in this PR to use T4 instead, but there'll still be a mix in the repo. |
|
|
||
| ``` | ||
| dotnet run | ||
| ``` No newline at end of file |
There was a problem hiding this comment.
Can we also add a test that fails if the generator and generated code get out of sync like Kestrel's GeneratedCodeIsUpToDate test?
| writer.WriteIndentedLine("public async Task ExecuteAsync(HttpContext httpContext)"); | ||
| writer.WriteIndentedLine("{"); | ||
| writer.WriteIndentedLine(2, "ArgumentNullException.ThrowIfNull(httpContext, nameof(httpContext));"); | ||
| writer.WriteLine(); | ||
| writer.WriteIndentedLine(2, "if (Result is null)"); | ||
| writer.WriteIndentedLine(2, "{"); | ||
| writer.WriteIndentedLine(3, "throw new InvalidOperationException(\"The IResult assigned to the Result property must not be null.\");"); | ||
| writer.WriteIndentedLine(2, "}"); | ||
| writer.WriteLine(); | ||
| writer.WriteIndentedLine(2, "await Result.ExecuteAsync(httpContext);"); |
There was a problem hiding this comment.
| writer.WriteIndentedLine("public async Task ExecuteAsync(HttpContext httpContext)"); | |
| writer.WriteIndentedLine("{"); | |
| writer.WriteIndentedLine(2, "ArgumentNullException.ThrowIfNull(httpContext, nameof(httpContext));"); | |
| writer.WriteLine(); | |
| writer.WriteIndentedLine(2, "if (Result is null)"); | |
| writer.WriteIndentedLine(2, "{"); | |
| writer.WriteIndentedLine(3, "throw new InvalidOperationException(\"The IResult assigned to the Result property must not be null.\");"); | |
| writer.WriteIndentedLine(2, "}"); | |
| writer.WriteLine(); | |
| writer.WriteIndentedLine(2, "await Result.ExecuteAsync(httpContext);"); | |
| writer.WriteIndentedLine("public Task ExecuteAsync(HttpContext httpContext)"); | |
| writer.WriteIndentedLine("{"); | |
| writer.WriteIndentedLine(2, "ArgumentNullException.ThrowIfNull(httpContext, nameof(httpContext));"); | |
| writer.WriteLine(); | |
| writer.WriteIndentedLine(2, "if (Result is null)"); | |
| writer.WriteIndentedLine(2, "{"); | |
| writer.WriteIndentedLine(3, "throw new InvalidOperationException(\"The IResult assigned to the Result property must not be null.\");"); | |
| writer.WriteIndentedLine(2, "}"); | |
| writer.WriteLine(); | |
| writer.WriteIndentedLine(2, "return Result.ExecuteAsync(httpContext);"); |
It's a little bit more efficient if we skip the tail await at the small cost of losing the stack frame if there are any exceptions.
This PR introduces just the
Results<TResult1, TResult2, TResultN>union types themselves. The types are code generated via a tool, along with their unit tests.When #40646 lands we can update the in-box
IResultimplementations to describe themselves via emitted metdata, and update theResults<TResult1, TResult2, TResultN>union types to pass through that metadata from theTResultNtype args, along with introducing the newResults.Typedfactory methods for creating results and preserving the concrete types.Until then, the union types allow all the possible
IResultreturn types of a route handler delegate to be declared explicitly and thus checked at compile for type safety, e.g.:Fixes #40672