Вы должны рассмотреть Microsoft Reactive Framework (Rx) для этого.Это намного проще и мощнее, чем использование задач.
Сначала я переписал ваш тестовый код для большей простоты:
public async Task<List<string>> GetAccountsAsync()
{
await Task.Delay(1500);
return new List<string> { "40701", "40702", "40703", "40704", "40705" };
}
private Random _rnd = new Random();
public async Task<int> AddAccount(string account)
{
await Task.Delay(1000);
if (_rnd.NextDouble() > 0.5)
{
return _rnd.Next(100);
}
else
{
Console.WriteLine("!");
throw new InvalidOperationException($"Account {account} was not added");
}
}
Теперь использование Rx для этого очень просто:
var query =
from accounts in Observable.FromAsync(() => GetAccountsAsync())
from account in accounts.ToObservable()
from id in Observable.Defer(() => Observable.FromAsync(() => AddAccount(account))).Retry(3)
select new { account, id };
Он лениво оценивается как LINQ, поэтому для его выполнения вы делаете следующее:
IDisposable subscription =
query
.Subscribe(
result => Console.WriteLine($"Account {result.account} created with id {result.id}"),
ex => Console.WriteLine($"Exception {ex.GetType().FullName} with \"{ex.Message}\"."),
() => Console.WriteLine("Completed Successfully"));
Чтобы отменить выполнение до его естественного завершения, просто вызовите subscription.Dispose()
.
Здесьнесколько примеров прогонов:
с ошибкой
Account 40705 created with id 63
throwing on 40704!
Account 40701 created with id 21
Account 40702 created with id 21
Account 40703 created with id 27
throwing on 40704!
throwing on 40704!
Exception System.InvalidOperationException with "Account 40704 was not added".
успешно завершено
throwing on 40703!
throwing on 40702!
Account 40701 created with id 25
Account 40704 created with id 88
Account 40705 created with id 26
Account 40703 created with id 43
Account 40702 created with id 98
Completed Successfully
Обратите внимание, что естьбыли некоторые ошибки, но оператор .Retry(3)
просто повторил попытку создания учетной записи и в итоге добился успеха.
Просто NuGet "System.Reactive" и используйте пространство имен System.Reactive.Linq
, чтобы заставить это работать.