如何通过HTTP服务高效传递大型Block Iterator可枚举对象?
Block Iterator是C#里的实用特性,但通过HTTP服务传递时必然要涉及序列化——就像你给出的示例代码那样,直接返回用Block Iterator实现的大数据集合,最终还是会把整个集合加载到内存里完成序列化,这在数据量很大的时候完全不现实。
下面是几种不用加载全量数据到内存就能解决问题的通用设计模式:
分页查询
这是最常用的方案,本质是把大数据集拆成多份分批返回。服务端调整接口,接收页码、每页条数这类参数,每次只从数据源(比如数据库)取对应页的数据返回。修改后的示例代码大概是这样:public ActionResult<IEnumerable<Something>> Get(int page = 1, int pageSize = 20) { return _provider.ReadLargeList(page, pageSize); }客户端只需要循环请求接口,直到返回的数据为空,就能拿到全量数据,全程不管是服务端还是客户端,都不用加载所有数据到内存。
流式传输
利用ASP.NET Core的流式响应能力,直接把Block Iterator生成的数据逐段序列化后写入响应流,不用先把整个集合序列化完再发送。可以直接操作响应流来实现:public async Task Get() { Response.ContentType = "application/json"; await using var writer = new StreamWriter(Response.Body); await using var jsonWriter = new JsonTextWriter(writer); jsonWriter.WriteStartArray(); foreach (var item in _provider.ReadLargeList()) { JsonSerializer.Serialize(jsonWriter, item); await jsonWriter.FlushAsync(); await writer.FlushAsync(); } jsonWriter.WriteEndArray(); }这种方式下,服务端会边生成数据边发送,内存里始终只存当前处理的那一条数据,客户端也需要支持流式读取响应。
游标式分页
适合有明确排序规则的数据集(比如按ID、时间戳排序)。服务端除了返回当前页的数据,还会返回一个“游标”(比如当前页最后一条数据的ID),客户端下次请求时带上这个游标,服务端就从游标对应的位置继续返回后续数据。示例代码可以写成:public ActionResult<(IEnumerable<Something> Items, string? NextCursor)> Get(string? cursor = null, int pageSize = 20) { var result = _provider.ReadLargeListAfterCursor(cursor, pageSize); return (result.Items, result.NextCursor); }这种方式比传统分页更靠谱,不会因为数据新增或删除出现重复或遗漏的问题,同样不用加载全量数据。
总的来说,这些方案的核心都是避免一次性把所有数据塞进内存,你可以根据自己的业务场景选最合适的——比如普通业务用分页就够了,实时数据流场景用流式传输,有序大数据集用游标分页。
内容的提问来源于stack exchange,提问作者Perfect28

