You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IAsyncEnumerable异步迭代器在async foreach中仅执行一次的问题修复

异步迭代器分批获取MongoDB数据仅执行一次的问题修正

问题根源

你的代码核心错误在于分页逻辑:用累计获取的条目数index作为Id的过滤条件,而MongoDB的文档Id(默认是ObjectId)并非连续递增的数字。当第一次查询返回数据后,index被设置为条目数量(比如10),后续查询会尝试找Id大于10的文档——但实际文档的Id是ObjectId类型,和数字10没有可比性,导致后续查询返回空集合,循环直接终止,所以await foreach仅执行一次。

修正方案

正确的分页逻辑应该是跟踪每一批数据的最后一条文档的实际Id,以此作为下一批查询的起始条件。

修正后的异步迭代器代码

public async IAsyncEnumerable<IEnumerable<Product>> GetProductsByChank(int chankSize)
{
    var collection = Database.GetCollection<Product>(ProductCollectionName);
    // 用可空类型处理第一批数据无前置Id的情况
    ObjectId? lastProcessedId = null;
    bool hasMoreData = true;

    while (hasMoreData)
    {
        // 构建过滤条件:第一批查全部,后续查Id大于上一批最后一条Id的文档
        var filter = lastProcessedId == null
            ? new BsonDocument()
            : Builders<Product>.Filter.Gt(IdFieldName, lastProcessedId);

        var currentBatch = await collection.Find(filter)
            .Limit(chankSize)
            .ToListAsync();

        hasMoreData = currentBatch.Count > 0;
        if (hasMoreData)
        {
            // 更新上一批最后一条的Id
            lastProcessedId = currentBatch.Last().Id;
            yield return currentBatch;
        }
    }
}

关键调整说明

  • 替换index计数逻辑为跟踪实际文档的Id:确保下一批查询的是真正的后续数据,而非基于错误的数字条件
  • 适配Id类型:如果你的Product.Id不是ObjectId(比如自增int),只需将ObjectId?替换为对应的类型(如int?)
  • 边界处理:空集合直接终止循环,避免无效的空返回

调用代码优化(可选)

原调用代码的强制转换是安全的,但可以更简洁:

var productBatches = new List<List<Product>>();
         
await foreach(var batch in _productMongoObserver.GetProductsByChank(10))
{
   productBatches.Add((List<Product>)batch);
}

内容的提问来源于stack exchange,提问作者Claus Stolz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 19:18:17