在 Core 2.2 中返回异步结果时无法使用 Ok(...) 作为状态代码

Not able to use Ok(...) for status code when returning an async'ed result in Core 2.2

我搭建了一个 WebAPI 模板,如下所示。

[HttpGet]
public async Task<ActionResult<IEnumerable<ThingVm>>> GetThings()
{
  Task<List<ThingVm>> output = Context.Things
    .Select(e => new ThingVm(e))
    .ToListAsync();
  return await output;
}

我注意到返回的结果不会传达任何状态代码(因为我没有提供 200 或 404 等)。所以我添加了标准的、通常的 Ok(...) 调用。

[HttpGet]
public async Task<ActionResult<IEnumerable<ThingVm>>> GetThings()
{
  ...
  return await Ok(output);
}

遗憾的是,电脑并没有领会到那个操作,还这样解释错误。

CS1061 'OkObjectResult' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'OkObjectResult' could be found (are you missing a using directive or an assembly reference?)

我明白为什么会这样。我不知道如何解决它同时保留随响应发出的状态代码。

我用谷歌搜索了它,但在 insanely old stuff, totally irrelevant stuff and rather unrelated stuff 上得到了点击。仅此而已 - 没有太多其他点击,这也令人惊讶。

如何发出状态代码并应用异步方法?它们并不相互排斥,对吗?

你为什么不等待结果呢?

[HttpGet]
public async Task<ActionResult<IEnumerable<ThingVm>>> GetThings()
{
  var output = await Context.Things
    .Select(e => new ThingVm(e))
    .ToListAsync();

  return Ok(output);
}

从评论来看,实际 问题似乎是如何将结果流式传输到客户端。这在 ASP.NET Core 2.2 中是不可能的。异步操作 (ToListAsync()) 必须首先完成:

[HttpGet]
public async Task<ActionResult<IEnumerable<ThingVm>>> GetThings()
{
  Task<List<ThingVm>> output = Context.Things
    .Select(e => new ThingVm(e))
    .ToListAsync();
  return await output;
}

随着 IAsyncEnumerable<T> 的引入,这在 .NET Core 3 中发生了变化。文章 What's the big deal with IAsyncEnumerable in .NET Core 3.0? 展示了如何一次返回所有结果的异步操作

public async Task<IEnumerable<Product>> GetAllProducts()

可以转换为:

[HttpGet]
public IAsyncEnumerable<Product> Get()
    => productsRepository.GetAllProducts();

ASP.NET 核心知道 IAsyncEnumerable<T> 并且会在每个结果对象到达时将其写入输出流。

EF Core 3也支持IAsyncEnumerable,也就是说GetThings()可以改成:

[HttpGet]
public IAsyncEnumerable<ThingVm> GetThings()
{
    var query = Context.Things
        .Select(e => new ThingVm(e))
        .AsAsyncEnumerable();
  return query;
}

AsAsyncEnumerable 没什么特别的,它只是将查询转换为 IAsyncEnumerable

这样做的一个好处是我们的代码不再需要在返回结果之前分配一个大列表或数组来保存结果

上线

.NET Core 3.0 Preview 7 有 Go Live license and can be used in production