Fre*_*ool 18 c# async-await asp.net-core-mvc asp.net-core
返回 anIAsyncEnumerable<T>和 aNotFoundResult但仍以异步方式处理的控制器操作的正确签名是什么?
我使用了这个签名并且它不能编译,因为它IAsyncEnumerable<T>是不可等待的:
[HttpGet]
public async Task<IActionResult> GetAll(Guid id)
{
try
{
return Ok(await repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
}
catch (NotFoundException e)
{
return NotFound(e.Message);
}
}
Run Code Online (Sandbox Code Playgroud)
这个编译得很好,但它的签名不是异步的。所以我担心它是否会阻塞线程池线程:
[HttpGet]
public IActionResult GetAll(Guid id)
{
try
{
return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
}
catch (NotFoundException e)
{
return NotFound(e.Message);
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试使用这样的await foreach循环,但显然也无法编译:
[HttpGet]
public async IAsyncEnumerable<MyObject> GetAll(Guid id)
{
IAsyncEnumerable<MyObject> objects;
try
{
objects = repository.GetAll(id); // GetAll() returns an IAsyncEnumerable
}
catch (NotFoundException e)
{
return NotFound(e.Message);
}
await foreach (var obj in objects)
{
yield return obj;
}
}
Run Code Online (Sandbox Code Playgroud)
Kir*_*kin 18
将 的实现传递IAsyncEnumerable<>到Ok调用中的选项 2很好。ASP.NET Core 管道负责枚举并IAsyncEnumerable<>从 3.0 开始感知。
这是问题的调用,重复上下文:
Run Code Online (Sandbox Code Playgroud)return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
调用Ok创建 的实例OkObjectResult,该实例继承ObjectResult. 传递给的值Ok是 类型object,它保存在ObjectResult的Value属性中。ASP.NET核心MVC使用命令模式,由此所述命令是的实现IActionResult和利用的实施方案中执行IActionResultExecutor<T>。
对于ObjectResult,ObjectResultExecutor用于将ObjectResult转换为 HTTP 响应。这是执行的ObjectResultExecutor.ExecuteAsync是IAsyncEnumerable<>知晓:
Run Code Online (Sandbox Code Playgroud)public virtual Task ExecuteAsync(ActionContext context, ObjectResult result) { // ... var value = result.Value; if (value != null && _asyncEnumerableReaderFactory.TryGetReader(value.GetType(), out var reader)) { return ExecuteAsyncEnumerable(context, result, value, reader); } return ExecuteAsyncCore(context, result, objectType, value); }
如代码所示,Value检查该属性以查看它是否实现IAsyncEnumerable<>(详细信息隐藏在对 的调用中TryGetReader)。如果是,ExecuteAsyncEnumerable则调用,它执行枚举,然后将枚举结果传递给ExecuteAsyncCore:
Run Code Online (Sandbox Code Playgroud)private async Task ExecuteAsyncEnumerable(ActionContext context, ObjectResult result, object asyncEnumerable, Func<object, Task<ICollection>> reader) { Log.BufferingAsyncEnumerable(Logger, asyncEnumerable); var enumerated = await reader(asyncEnumerable); await ExecuteAsyncCore(context, result, enumerated.GetType(), enumerated); }
reader在上面的片段中是枚举发生的地方。它被掩埋了一点,但你可以在这里看到来源:
Run Code Online (Sandbox Code Playgroud)private async Task<ICollection> ReadInternal<T>(object value) { var asyncEnumerable = (IAsyncEnumerable<T>)value; var result = new List<T>(); var count = 0; await foreach (var item in asyncEnumerable) { if (count++ >= _mvcOptions.MaxIAsyncEnumerableBufferLimit) { throw new InvalidOperationException(Resources.FormatObjectResultExecutor_MaxEnumerationExceeded( nameof(AsyncEnumerableReader), value.GetType())); } result.Add(item); } return result; }
所述IAsyncEnumerable<>被枚举为List<>使用await foreach,其中,几乎从定义,不会阻塞的请求线程。正如 Panagiotis Kanavos 在对 OP 的评论中指出的那样,在将响应发送回客户端之前,会完整执行此枚举。
| 归档时间: |
|
| 查看次数: |
3238 次 |
| 最近记录: |