在 foreach 循环中等待的正确方法

The correct way to await inside a foreach loop

这是使用 async 使用 foreach 循环的正确方法吗?有没有更好更有效的方法? IAsyncEnumerable? (忽略两个表可以连接在一起的事实这是一个简单的例子)

public async Task<IList<ContactAndAccounts>> GetAll()
{
    var accounts = await _dbContext.Account.Where(x => x.Name == "Amazing").ToListAsync();

    foreach(var account in accounts)
    {
           accounts.Contacts = await GetContact(account.Id);
    }

    return accounts;
}

public async Task<IList<contact>> GetContact(Guid id)
{
    return await _dbContext.Contact.Where(x => x.AccountLinkId = id).ToListAsync();
}

这绝对不是最有效的方法。

每个 await 都会导致循环暂停,直到 Task 完成。

您想允许所有任务同时 运行:

public async Task<IList<ContactAndAccounts>> GetAll()
{
    var accounts = await _dbContext.Account.Where(x => x.Name == "Amazing").ToListAsync();

    await Task.WhenAll(accounts.Select(async account => 
    {
        accounts.Contact = await GetContact(account.Id);
    }));

    return accounts;
}

Select 将为每个项目产生一个 Task,可以通过 Task.WhenAll.

等待

我同意 但我会说,从数据库的角度来看,您可能会发现执行单个查询大查询比执行大量小查询更快。

执行一个查询并传入所有 ID 通常比多个单独的查询更便宜。

public async Task<IList<ContactAndAccounts>> GetAll()
{
    var accounts = await _dbContext.Account.Where(x => x.Name == "Amazing").ToListAsync();

    var contacts = await GetContacts(accounts.Select(o => o.Id));

    // Map contacts to accounts in memory

    return accounts;
}

public async Task<IList<contact>> GetContacts(List<Guid> ids)
{
    return await _dbContext.Contact.Where(x => ids.Contain(x.AccountLinkId)).ToListAsync();
}

Is there a better more efficient way? IAsyncEnumerable?

是的!更改签名并将 accounts.Contacts = await GetContact(account.Id); 替换为 yield return await GetContract(account.Id).