【问题标题】:.NET MAUI, returning IAsyncEnumerable from Api call in another method, how to dispose of the HttpResponse?.NET MAUI,在另一个方法中从 Api 调用返回 IAsyncEnumerable,如何处理 HttpResponse?
【发布时间】:2022-12-24 09:11:07
【问题描述】:

在 Maui Xaml 应用程序中,我有一个页面及其视图模型,其中包含我想通过 api 调用检索的项目的 ObservableCollection。 我想在另一个服务中进行 api 调用,其中放置了所有相同的 api 调用。 顺便说一下,我正在使用 Maui CommunityToolkit mvvm 等,所以 InitializeCommandeventToCommmandBehaviour OnAppearing 绑定到 UI。

我想要实现的是,项目在到达时被添加,而不是像通常读取响应和具体化数组中的项目那样成块。

在我的视图模型中:

private async Task InitializeCommand()
{
    items.Clear();
    var result = await _apiService.GetItemsAsync();
    await foreach(var item in result)
    {
        items.Add(item);
    }
}

apiService 方法执行此操作:

public async Task<IAsyncEnumerable<Item>> GetItemsAsync()
{
     using var client = httpClientFactory.CreateClient();
     using var response = await client
        .GetAsync("myUrl", HttpCompletionOption.ResponseHeadersRead)
     using var stream = await response.Content.ReadAsStreamAsync();
     return JsonSerializer.DeserializeAsyncEnumerable<Item>(stream);
}

现在这个方法有问题,using关键字。首先,我不确定是否需要为 clientresponsestream 指定 3 次。 但是除此之外,通过在 response 超出范围时立即处理它,在 InitializeCommand 方法中使用它时会出现异常! 我不确定如何以正确的方式处理所有这些请求,而不是在调用方进行处理。 在继续之前调用await.Response.Content.ReadAsStreamAsync();是否等待所有流被读取?我不想这样,我希望在读取标头后立即开始对 IAsyncEnumerable 的反序列化。 我应该返回流并在调用方法中反序列化它然后处理它吗?

【问题讨论】:

  • ReadAsStreamAsync 医生说“返回的 Task<TResult> 对象将在读取了表示内容的所有流后完成。”听起来像你不想要的行为。虽然我确实看到提到子类能够以不同的方式实现,但不清楚 HttpResponse 的作用。你能写你自己的 IAsyncEnumerable&lt;T&gt; 方法,直到响应完成才结束吗?因此 Dispose 在资源耗尽之前不会发生。返回 IEnumerable&lt;T&gt; 将是 foreach (var item ...) yield return item;,但我不知道如何为 IAsyncEnumerable 执行此操作。

标签: c# asp.net .net xamarin maui


【解决方案1】:

我希望在读取标头后立即开始对 IAsyncEnumerable 的反序列化

您的意思是要同时运行以下两行代码吗?

using var stream = await response.Content.ReadAsStreamAsync();
return JsonSerializer.DeserializeAsyncEnumerable<Item>(stream);

那应该是不可能的,本金就像读写锁一样。一个对象不能同时写入和读取。第一行代码写入变量stream,第二行代码读取它。

我不确定如何以正确的方式处理所有这些请求

关于using关键字和对象dispose,可以参考this case。如果你不想使用它,你可以将对象var stream传递给你想再次使用它的方法,并调用stream.Dispose()来处理它。

【讨论】:

    【解决方案2】:

    您的处理问题很可能是因为您使用的是 using(非异步感知)而不是 await using(异步感知)。

    以下代码在 net7.0 中开箱即用:

    public async IAsyncEnumerable<T?> GetItemsAsync<T>(string url)
    {
        using HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
        await using Stream stream = await response.Content.ReadAsStreamAsync();
    
        await foreach (T? item in JsonSerializer.DeserializeAsyncEnumerable<T>(stream, serializerOptions))
        {
            yield return item;
        }
    }
    

    然而,在 net6.0 中,缓冲的工作方式有所不同,它不会流式传输!我没有深入研究原因,但我怀疑两者之间存在默认配置差异(或错误修复)。

    这里有完整的工作示例(客户端、服务器、合同):https://github.com/prosser/UsefulSamples/tree/main/StreamingIAsyncEnumerable

    【讨论】:

      猜你喜欢
      • 2017-02-05
      • 1970-01-01
      • 1970-01-01
      • 2020-04-28
      • 2021-02-19
      • 1970-01-01
      • 2021-05-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多