等待数据时的 `WaitAll()` 或 `WhenAll`

`WaitAll()` or `WhenAll` when expecting data

我从未尝试在 运行 async 功能时使用 WaitAll()WhenAll()。在查看了许多文档、SO 帖子和教程之后,我还没有找到足够的信息,所以我来了。

我正在尝试找出 best/proper 方法来执行以下操作:

使用 EF6,获取数据为 List<Entity>。 遍历每个 Entity 并调用外部 API 来执行某些操作。 每个 Entity 的外部 API returns 数据我需要存储在同一个 Entity.

目前我已经构建(未测试)以下(没有错误处理代码):

public IEnumerable<Entity> Process() {
    bool hasChanged = false;
    var data = _db.Entity.Where(x => !x.IsRegistered);
    
    foreach (var entity in data) {
        var result = await CallExternalApi(entity.Id, entity.Name);

        entity.RegistrationId = result.RegistrationId;
        entity.IsRegistered = true;
        _db.Entry(entity).State = EntityState.Modified;
        hasChanges = true;
    }

    if (hasChanges) {
        uow.Commit();
    }

    return data;
}

我觉得我可以利用 async 中的其他一些 functionality/feature,但如果可以的话,我不确定如何在这里实现它。

非常感谢任何指导。

更新

我正在呼叫的 API 是用于添加注册人的 Zoom Api。虽然他们确实有批量添加注册人的途径,但它没有 return 我需要的 RegistrantId 和 Join Url。

首先,确定您的外部 API 是否有办法批量获取您想要的所有项目。如果是,请使用它而不是发送一大堆请求。

如果您需要为每个项目发送一个单独的请求,但又想同时进行,您可以这样做:

public async Task<IReadOnlyCollection<Entity>> Process() {
    var data = _db.Entity.Where(x => !x.IsRegistered).ToList();

    if(!data.Any()) { return data; }

    var entityResultTasks = data
        .Select(async entity => new { entity, result = await CallExternalApi(entity.Id, entity.Name) })
        .ToList();
    var entityResults = await Task.WhenAll(entityResultTasks);
    foreach (var entityResult in entityResults) {
        var entity = entityResult.entity;
        var result = entityResult.result;

        entity.RegistrationId = result.RegistrationId;
        entity.IsRegistered = true;
        _db.Entry(entity).State = EntityState.Modified;
    }
    uow.Commit();
    return data;
}

您需要注意目标源上可能存在的并发限制。考虑使用 Chunk 将您的工作分成大小可接受的批次,或利用信号量或其他东西来限制您进行的调用次数。