Правильный способ ожидания внутри цикла foreach - PullRequest
1 голос
/ 30 января 2020

Это правильный способ использования циклов foreach висте с использованием asyn c? Есть ли лучший, более эффективный способ? 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();
}

Ответы [ 3 ]

4 голосов
/ 30 января 2020

Я согласен с ответом Джонатана Барклая , но скажу, что с точки зрения базы данных вы можете обнаружить, что большой запрос выполняется одним запросом быстрее, чем множество небольших запросов.

Выполнение одного запроса запрос и передача всех ваших идентификаторов обычно дешевле, чем несколько отдельных запросов.

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();
}
0 голосов
/ 30 января 2020

Есть ли лучший, более эффективный способ? IAsyncEnumerable?

Да! Измените подпись и замените accounts.Contacts = await GetContact(account.Id); на yield return await GetContract(account.Id).

0 голосов
/ 30 января 2020

Это определенно не самый эффективный способ.

Каждый await заставляет l oop делать паузу до завершения 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.

...