0

I previously posted a question about using HTTPClient with async/await. Now I'm trying to figure out how to do this in such a way as to actually make the Post calls execute at the same time while still being able to handle the resulting HttpResponseMessage.

This is what I've come up with. However, being a noob to await/async, I'm still unsure about if I'm doing this correctly or not. Could someone verify that this is the proper way to do this ... or at least a proper way.

public async Task ProcessAsync() {
   //Query lists
   List<Member> list = dbContext.Users.Where(u => u.IsMember).ToList();
   //Add members to mailing list through web service
   await AddMembersAsync(list);
 }

 private async Task AddMembersAsync(List<Member> members) {
     using(var _client = new HttpClient()) {
         //Initialize Http Client
         ...
         var responses = await Task.WhenAll(members.Select(x => PostMemberAsync(x,_client)));
         await Task.WhenAll(responses.Select(r => ProcessResponseAsync(r,client)));
     }
 }

 private async Task<HttpResponseMessage> PostMemberAsync(Member member, HttpClient client) {
    var jss = new JavaScriptSerializer();
    var content = jss.Serialize(new MemberPost() {
       email_address = member.email,
       ... 
    });
    return await client.PostAsync("uri",new StringContent(content, Encoding.UTF8, "application/json"));
 }

 private async Task ProcessResponsesAsync(HttpResponseMessage response, HttpClient client) {
    if(response.IsSuccessStatusCode) {
       var responseText = await response.Content.ReadAsStringAsync();
       var jss = new JavaScriptSerializer();
       var userid = jss.Deserialize<MemberResponse>(responseText);
       //Store mailing user's id
       ...
    }
    response.Dispose();
 }

This appears to me like it would be correct. However, I have a slight problem with this. I need to tie each HttpResponseMessage to the member for which the message was created. My current code only returns Task but the response message does not contain a link to the user. (The service I'm posting to returns an id specific to the service. I need to keep track of that Id for each user so that I have a link between the member id and the service id).

Does my requirement of linking the id from the response message to the member make it unrealistic to use the above code or is there a way to somehow return the member as part of the task results?

Community
  • 1
  • 1
RHarris
  • 10,641
  • 13
  • 59
  • 103

2 Answers2

1

I'm suggesting this without trying it out so please be careful but I would replace these two lines:

var responses = await Task.WhenAll(members.Select(x => PostMemberAsync(x,_client)));
     await Task.WhenAll(responses.Select(r => ProcessResponseAsync(r,client)));

with this:

await Task.WhenAll(members.Select(async x => 
{ 
   var response = await PostMemberAsync(x, _client);
   await ProcessResponseAsync(response, client, x);
}));

And of course you need to enhance ProcessResponseAsync by the argument Member

Manuel Zelenka
  • 1,616
  • 2
  • 12
  • 26
0

I need to tie each HttpResponseMessage to the member for which the message was created.

When doing asynchronous programming, I find it useful to avoid side effects. In other words, if you have a method that calculates or determines something, return that value from the method rather than saving it in some member variable.

private async Task<MemberResponse> ProcessResponseAsync(HttpResponseMessage response, HttpClient client)
{
  using (response)
  {
    if(response.IsSuccessStatusCode)
    {
      var responseText = await response.Content.ReadAsStringAsync();
      var jss = new JavaScriptSerializer();
      var userid = jss.Deserialize<MemberResponse>(responseText);
      return userid;
    }
    else
    { ... }
  }
}

Add a small helper method, and your calling code becomes quite clean:

private async Task<HttpResponseMessage> ProcessMemberAsync(Member member, HttpClient client)
{
  var response = await PostMemberAsync(member, client);
  return await ProcessResponseAsync(response, client);
}

private async Task AddMembersAsync(List<Member> members)
{
  using(var client = new HttpClient())
  {
    ... // Initialize HttpClient
    var responses = await Task.WhenAll(members.Select(x => ProcessMemberAsync(x, client)));
    for (int i = 0; i != members.Count; ++i)
    {
      var member = members[i];
      var response = responses[i];
      ...
    }
  }
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810