简体   繁体   English

使用 IAsyncEnumerable

[英]Consuming an IAsyncEnumerable

I have one web API application that has a function that needs to stream some data.我有一个 web API 应用程序,它有一个 function 需要 stream 一些数据。 Then I have another web application that basically acts like a proxy for that.然后我有另一个 web 应用程序,基本上充当它的代理。 This other application does a whole lot of other stuff like handling authorization and such.这个另一个应用程序做很多其他的事情,比如处理授权等等。 It needs to connect to the API endpoint and pass the stream through potentially doing some addition processing for each row of data.它需要连接到 API 端点并通过可能对每一行数据进行一些添加处理来传递 stream。 In one case it will need to write out that data into an Excel file, for example.例如,在一种情况下,它需要将该数据写出到一个 Excel 文件中。

Unfortunately, I see a lot of tutorials on how to create an IAsyncEnumerable, but not any about actually connecting to an endpoint that exposes one.不幸的是,我看到很多关于如何创建 IAsyncEnumerable 的教程,但没有任何关于实际连接到公开端点的教程。

Ideally, I would like to actually wrap my IAsyncEnumerable and return it with some other stuff .理想情况下,我想实际包装我的 IAsyncEnumerable 并将其与其他一些东西一起返回。 This is what I basically do with other non-streamed endpoints already:这就是我基本上已经对其他非流媒体端点所做的事情:

public class NonStreamedResult : BaseResult
{
    public SomeMetaData MetaData {get; set; }
    // here Data is usually small, so returning it all at once is no big deal
    public IEnumerable<SomeData> Data {get; set; }  
}

//....controller
public async Task<NonStreamedResult> GetNonStreamedData()

And on the consuming side, I'm using an HttpClient , awaiting an HttpResponseMessage and using ReadAsAsync<T> to turn that response into an object.在消费方面,我正在使用HttpClient ,等待HttpResponseMessage并使用ReadAsAsync<T>将该响应转换为 object。

However, if I try to do this:但是,如果我尝试这样做:

public class StreamedResult : BaseResult
{
    public SomeOtherMetaData MetaData {get; set; }
    // Here data could be huge, so we don't want to have to hold it all in memory at once
    public IAsyncEnumerable<SomeData> Data {get; set; }
}

//....controller
public async Task<StreamedResult> GetStreamedData()

I will get an error when it tries to deserialize because it doesn't know what concrete class to convert the IAsyncEnumerable into.当它尝试反序列化时我会得到一个错误,因为它不知道将IAsyncEnumerable转换成什么具体的 class。

So what's the right way to connect to an endpoint that is using an IAsyncEnumerable without loading the entire response into memory (obviously) so that I can eventually return another IAsyncEnumerable from the proxy app?那么连接到使用IAsyncEnumerable的端点而不将整个响应加载到 memory(显然)以便我最终可以从代理应用程序返回另一个IAsyncEnumerable的正确方法是什么?

It would be nice if I could still pass it wrapped, but it's not the end of the world if I have to work with just the naked IAsyncEnumerable如果我仍然可以将它包裹起来,那就太好了,但如果我必须只使用裸体IAsyncEnumerable ,那还不是世界末日

Note that I'm using the Microsoft.Bcl.AsyncInterfaces NuGet package since I'm still targeting an earlier version of the .NET framework.请注意,我使用的是Microsoft.Bcl.AsyncInterfaces NuGet package,因为我仍然以 .NET 框架的早期版本为目标。

I'm still targeting an earlier version of the .NET framework.我仍然以 .NET 框架的早期版本为目标。

That's not going to work well.那不会很好地工作。 Unbuffered IAsyncEnumerable<T> support was added in ASP.NET 6 . ASP.NET 6 中添加了无缓冲IAsyncEnumerable<T>支持

If you can't upgrade, then you'll need to write your own streaming JSON parser and your own streaming JSON serializer wrapped up in a custom formatter that flushes periodically.如果您无法升级,那么您将需要编写自己的流式 JSON 解析器和您自己的流式 JSON 序列化程序,并将其封装在定期刷新的自定义格式化程序中。 This is a non-trivial amount of work.这是一项非常重要的工作。 The remainder of my answer assumes you will move to .NET 6.我回答的其余部分假设您将移动到 .NET 6。

It would be nice if I could still pass it wrapped, but it's not the end of the world if I have to work with just the naked IAsyncEnumerable如果我仍然可以将它包裹起来,那就太好了,但如果我必须只使用裸体 IAsyncEnumerable,那还不是世界末日

Wrapped can't possibly work.包裹不可能工作。 Just consider the situation where you define multiple IAsyncEnumerable<T> properties.只需考虑定义多个IAsyncEnumerable<T>属性的情况。 When dealing with IAsyncEnumerable<T> in ASP.NET 6, you must return it as a top-level type.在处理 ASP.NET 6 中的IAsyncEnumerable<T>时,必须将其作为顶级类型返回。 Metadata can be represented as headers.元数据可以表示为标题。

To consume, first ensure that you set the HttpClient completion options so that the HTTP operation is considered "complete" when the headers are received, and then you can take the response body stream and pass it to JsonSerializer.DeserializeAsyncEnumerable .要使用,首先确保设置HttpClient完成选项,以便在收到标头时将 HTTP 操作视为“完成”,然后您可以获取响应主体 stream 并将其传递给JsonSerializer.DeserializeAsyncEnumerable After retrieving each item from the downstream API, you can do whatever processing you need and then yield it to be streamed to your client.从下游yield检索每个项目后,您可以进行任何需要的处理,然后将其流式传输给您的客户端。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM