Generate a real async iterator for IAsyncEnumerable actions #13187 - #13191
Conversation
…erable actions A controller action returning IAsyncEnumerable<T> produced a proxy that did not compile: the method came out `async` with the iterator's return type and a `return` statement (CS1622), wrapped in prerenderStateService.GetValue, which stores one resolved value per url and cannot hold a stream. The streamed path now yields the items through, keeps the request and response alive for the enumeration with `using`, skips the prerender state, and marks the cancellation token with EnumeratorCancellation so a WithCancellation at the call site reaches the enumerator. WrapWithResponseDisposal goes with it - the response's `using` now lives inside the iterator. Every other action is generated exactly as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…or path doesReturnIAsyncEnumerable is computed from the unwrapped return type, so Task<IAsyncEnumerable<T>> and ValueTask<IAsyncEnumerable<T>> set it too - and a method declared Task<...> cannot be an iterator. Only a method whose own return type is the stream takes the new path; the wrapped shapes keep the behaviour they had, which is why WrapWithResponseDisposal comes back. Verified against Boilerplate with the three shapes: Task<string[]>, IAsyncEnumerable<string> and Task<IAsyncEnumerable<string>> all build, and each generates what it should. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e encoded Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 4 included reviews per hour; 1 remains after this review. WalkthroughThe source generator now identifies direct ChangesAsync stream proxy generation
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to Direct IAsyncEnumerable proxy actions now compile and stream response items while keeping the response alive through enumeration. No concrete merge-blocking risk remains. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. A rabbit reads each line, Comment |
Closes #13187.
An
IAsyncEnumerable<T>action generated a proxy that did not compile (CS1622): anasyncmethod with the iterator's return type and areturnstatement, wrapped inprerenderStateService.GetValue— which stores one resolved value per url and cannot hold a stream.The streamed path is now a real async iterator:
[EnumeratorCancellation]on the token, so aWithCancellationat the call site reaches the enumeratorusingmoves inside the iterator, which makesWrapWithResponseDisposalredundant — removedJsonTypeInfois still passed, so trimming and AOT are unaffectedEvery other action is generated exactly as before.
Verified by building the generator and pointing
Boilerplate.Sharedat it in place of the package, withIDiagnosticController.PerformDiagnosticdeclared asIAsyncEnumerable<string>: the project builds, the emitted method is the one above, and the other 62 generated methods still go throughprerenderStateServiceunchanged.🤖 Generated with Claude Code
Summary by CodeRabbit