Reputation: 363
I need to make multiple webrequests where URIs are in a DataTable. Earlier I had the below code. But I realized this makes synchronous calls as await would wait till GET/POST call is complete and response is processed then it proceeds to next iteration.
foreach (DataRow dr in dt.Rows)
{
activeTasks.Add(SendRequestAsync(dr));
Task.WhenAll(activeTasks).Wait();
}
private async Task<string> SendRequestAsync(DataRow dr)
{
using (var client = new HttpClient())
{
string reqMethod = (dr["RequestMethod"] != null && dr["RequestMethod"].ToString() != "") ? dr["RequestMethod"].ToString() : "GET";
client.BaseAddress = new Uri(dr["URL"].ToString());
client.DefaultRequestHeaders.Accept.Clear();
string reqContentType = (dr["RequestContentType"] != null && dr["RequestContentType"].ToString() != "") ? dr["RequestContentType"].ToString() : "text/xml";
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(reqContentType));
HttpResponseMessage response = null;
try
{
if (reqMethod == "GET")
response = await client.GetAsync(client.BaseAddress.AbsoluteUri);
else
response = await client.PostAsync(client.BaseAddress.AbsoluteUri, null);
response.EnsureSuccessStatusCode();
var responseText = await response.Content.ReadAsStringAsync();
return responseText;
}
catch (Exception e)
{
return "-1";
}
}
}
Then I came across Parallel feature and used Parallel.ForEach instead. Like this:
Parallel.ForEach(rows, dr =>
{
activeTasks.Add(SendRequestAsync(dr));
Task.WhenAll(activeTasks).Wait();
});
This works fine, parallelism is achieved, requests are asynchronous and it completes within fraction of a time as compared to earlier solution. But the problem is it is not reliable - at times I get errors like
Is there anyway we can achieve http async calls within a foreach?
Upvotes: 1
Views: 2983
Reputation: 7010
As @Johnathon_Chase said, just move your WhenAll()
call outside of the loop:
foreach (DataRow dr in dt.Rows)
{
activeTasks.Add(SendRequestAsync(dr));
}
Task.WhenAll(activeTasks).Wait();
The for
loop populates the collection, and then Task.WhenAll()
blocks while the requests complete.
Upvotes: 7
Reputation: 29981
Parallel.ForEach
is for CPU-intensive operations and is not designed for I/O-intensive operations or for async.
You can await
inside of a foreach
loop just fine. The method containing your loop needs to be async itself, though.
Upvotes: 4