C# TAP programming inside iterations
The following is an example where we need to call and await an external API multiple times inside an iteration.
I’m using myFakeAPI from postman for this example and one of their Car
response look like this
public class CarResponse
{
public CarDto Car { get; set; }
}
public class CarDto
{
public int Id { get; set; }
public string Car { get; set; }
public string Car_Model { get; set; }
public string Car_Color { get; set; }
public int Car_Model_Year { get; set; }
public string Car_Vin { get; set; }
public string Price { get; set; }
public bool Availability { get; set; }
}
Then this is the method which does call and mapping
private async Task<CarResponse> ExecuteCall(string id)
{
string combinedUrl = URL + id;
using var response = await _httpClient.GetAsync(combinedUrl);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<CarResponse>(json);
}
Control
This is the control version where we launch and await the tasks one at a time
// DON'T DO THIS
private async Task<List<CarResponse>> Control()
{
List<CarResponse> carList = [];
foreach (string id in _idsList)
{
CarResponse singleCar = await ExecuteCall(id);
carList.Add(singleCar);
}
return carList;
}
Task.WhenAll()
It’s the most simple one - all tasks are launched at the same time. It’s ideal when we don’t have limits as we have no control over the simultaneous number of calls
// simple but what if we'd have +100 calls?
private async Task<List<CarResponse>> TaskWhenAll()
{
var getCarsTask = _idsList.Select(ExecuteCall);
var cars = await Task.WhenAll(getCarsTask);
return cars.ToList();
}
Parallel.ForEachAsync()
(.NET6+) this gives us the most control over number of parallel calls. It’s more complex.
private async Task<List<CarResponse>> ParallelForEachAsync()
{
// this is a secure collection for multiple threads
var carsBag = new ConcurrentBag<CarResponse>();
var options = new ParallelOptions { MaxDegreeOfParallelism = 5 };
await Parallel.ForEachAsync(_idsList, options, async (id, ct) =>
{
CarResponse car = await ExecuteCall(id);
carsBag.Add(car);
});
return carsBag.ToList();
}
Results
For 50 calls:
- control took 19s
Task.WhenAll()
took 0.9sParallel.ForEachAsync
with a degree of parallelism of 10, took 1.5s
As we see both of them are a great improve over control.