Home > Enterprise >  Multiple Async calls, how to deal with responses in a way that make sense
Multiple Async calls, how to deal with responses in a way that make sense

Time:12-16

I am making multiple async calls to a couple of different URLS, both urls should return the same result but i would like to compare the results from both or check for certain values in the responses. i am not sure how to compare or look for specific values in the responses outside of status codes, is there an easy way to do this? also would like to take note of the response and if it was a failure i want to be able to keep track of that later in my code to not use that url again and im not sure how i would go about this

Code:

private async Task<ClientModel> getClientInfoAsync(string clientID)
    {
        
        ClientModel c = null;
       
        try
        {
            
            var client = new HttpClient();
            //Start requests for all of them
            var requests = urls.Select
                (
                url => client.GetAsync(getURL(url, "Client", clientID))
                ).ToList();
            //Wait for all the requests to finish
            await Task.WhenAll(requests);

            //Get the responses
            var responses = requests.Select
                (
                    task => task.Result
                );
           
            foreach (var r in responses)
            {
                
                // Extract the message body
                var s = await r.Content.ReadAsStringAsync();                    
                          
                if (r.IsSuccessStatusCode)
                {
                    c = r.Content.ReadAsAsync<ClientModel>().Result;                        
                    SetLastSuccessfulCommunicationDetails();  //after this call HERE I THINK IS WHERE I WOULD COMPARE RESPONSES AND GO FROM THERE                     

                }
                
            }
           
        }
        catch (Exception ex)
        {
            string errMsg = "Error getting the client info";
            //...catch error code here...
        }
        
        return c;
    }

Basically im unsure of how to deal with the responses and only return one client model (c) based on my comparison and status of the response. let me know if i need to include any further information

CodePudding user response:

First of all, don't use .Result if you can help it. There's a cleaner way to grab all the responses.

var responses = await Task.WhenAll(requests);

One down-side to that approach is that it'll throw an exception if any of the requests throws an exception. So if you want to intelligently handle that case you'll need to try/catch and have some special logic to deal with that.

You can continue that pattern for each asynchronous step, to deal with your results as collections:

        var validResponses = responses.Where(r =>r.IsSuccessStatusCode);
        var clientModels = await Task.WhenAll(
            validResponses
                .Select(async r => await r.Content.ReadAsAsync<ClientModel>()));

One down-side to this approach is that you end up waiting for all the requests to complete before you start reading any of the response content. So it might make more sense to put the whole process of issuing a request, getting its response, and collecting any data along the way into a single async method, and just say var responseData = await Task.WhenAll(urls.Select(ThatMethod)). That way, as soon as one request comes back you can start consuming its response.

When it comes to comparing the results, it's impossible to help without knowing what kind of comparison you're looking for. But let's just say you want the one with the highest value for some property on the model, for instance:

        var lastResponseModel = clientModels
            .OrderByDescending(c => c.LastSaved)
            .First();
        return lastResponseModel;

CodePudding user response:

If I can assume that you have a method that looks like this:

private Task<ClientModel> DetermineClientModelFromResponses(IEnumerable<string> responses)

...then you can use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq;.

It let's you do this:

private async Task<ClientModel> GetClientInfoAsync(string clientID) =>
    await DetermineClientModelFromResponses(
        await Observable.Using(
            () => new HttpClient(),
            client =>
                urls
                    .ToObservable()
                    .SelectMany(url => Observable.FromAsync(() => client.GetAsync(getURL(url, "Client", clientID))))
                    .Where(response => response.IsSuccessStatusCode)
                    .SelectMany(response => Observable.FromAsync(() => response.Content.ReadAsStringAsync()))
                    .ToArray()));
                

...or, alternatively, this:

private async Task<ClientModel> GetClientInfoAsync(string clientID) =>
    await DetermineClientModelFromResponses(
        await Observable.Using(
            () => new HttpClient(),
            client =>
            (
                from url in urls.ToObservable()
                from response in Observable.FromAsync(() => client.GetAsync(getURL(url, "Client", clientID)))
                where response.IsSuccessStatusCode
                from text in Observable.FromAsync(() => response.Content.ReadAsStringAsync())
                select text
            ).ToArray()));
  • Related