0

Given a certain number of request objects (max 9), i need to call a web service endpoint the same number of times asynchronously. With .NET 4.0, we used delegate and IAsyncResult to achieve this.

Is there a better way to do this with asyc/await, TPL or both of them combined with .NET 4.6.1?

Will using Parallel.ForEach with ConcurrentBag be optimal as suggested in this answer?

Synchronous Code Example:

public List<WbsResponse> GetWbsResults()
{
    List<WbsRequest> requests = CompileWbsRequests();
    List<WbsResponse> results = new List<WbsResponse>();
    foreach (var request in requests)
    {
        //Call same web service endpoint n number of times
        var response = CallWebService(request);
        results.Add(response);
    }

    //do something with results

    return results;
}

private WbsResponse CallWebService(WbsRequest request)
{
    //Call web service
}

Edit/Update 1: Based on @Thierry's answer, i've created a sample code assuming there's an Order property in both the request and response objects to mark the request/response ordering:

public List<WbsResponse> GetWbsResults()    
{        
    List<WbsRequest> requests = CompileWbsRequests();
    List<WbsResponse> results = new List<WbsResponse>();

    Parallel.ForEach(requests, (request) => {
        var response = CallWebService(request);
        response.Order = request.Order;
        results.Add(response);
    });

    results = results.OrderBy(r => r.Order).ToList();

    //do something with results

    return results;
}

private WbsResponse CallWebService(WbsRequest request)
{
    //Call web service
}

Edit/Update 2: Based on this thread, i've made a few changes to Update 1:

await Task.Run(() => {
    Parallel.ForEach(requests, (request) => {
        var response = CallWebService(request);
        response.Order = request.Order;
        results.Add(response);
    });
});

Requirement Summary:

  • Make multiple web service requests asynchronously to the same endpoint with different parameters.
  • Add web service results to a list in the same order as the request was made (as if it was synchronous).
Sang Suantak
  • 5,213
  • 1
  • 27
  • 46

2 Answers2

1

Because each task finish with diffrent moment, I think you should numero the request and ordered the responses by this numero.

In the request, you init a numero and pass this numero for the response associated. Finally, when I have the results, I order it. Like this:

    public async Task<List<WbsResponse>> GetWbsResults()
    {
        List<WbsRequest> requests = CompileWbsRequests();
        List<Task<WbsResponse>> tasks = new List<Task<WbsResponse>>();
        for (var i = 0; i < requests.Count; i++)
        {
            var task = new Task<WbsResponse>(() => { CallWebService(WbsRequest); });
            tasks.Add(task);
        }
        var responses = await Task.WhenAll(tasks);

        var responsesOrdered = responses.OrderBy(r => r.Order)

        //do something with results

        return results;
    }  



    public List<WbsRequest> CompileWbsRequests()
    {
        //create requests
        foreach(var request in requests)
        {
            request.Order += 1;
        }
    }


    private WbsResponse CallWebService(WbsRequest request)
    {
        //Call web service

        reponse.order = request.order;
        return reponse;
    }
Antoine V
  • 6,998
  • 2
  • 11
  • 34
  • You cannot use await without async – Sir Rufo Jul 06 '18 at 08:15
  • @SirRufo : Thanks for your comment. I updated the answer. – Antoine V Jul 06 '18 at 08:16
  • My C# compiler only accepts `async void` or `async Task` or `async Task<>` - maybe you have a special one – Sir Rufo Jul 06 '18 at 08:19
  • @SirRufo non, you were totally right, it should always async because I wrote the code directly on StackOverflow without a compiler with juste the idea how to work. I missed async, it's my mistake :) – Antoine V Jul 06 '18 at 08:21
  • `async List` will never compile but `async Task>` would – Sir Rufo Jul 06 '18 at 08:37
  • @ThierryV i've updated my question with a sample code based on your code. It looks cleaner than the `Task` based code. However, i'm not sure if this is efficient. – Sang Suantak Jul 06 '18 at 10:00
  • @SangSuantak I believe that it should work because ``Task.WhenAll(tasks)`` gives you an array of all task executed. With a simple order by, the results should be in order. – Antoine V Jul 06 '18 at 10:03
  • @ThierryV, i've made further updates to my question based on another source. Will accept your answer since it pointed me to the right direction. – Sang Suantak Jul 06 '18 at 10:15
0

I think you can use Task.WaitAll to make the code work in async way and it will look prettier as well:

public List<WbsResponse> GetWbsResults()
{
    List<WbsRequest> requests = CompileWbsRequests();
    var responses = await Task.WhenAll(requests.Select(CallWebService));
    return responses;
}

but you have to modify this method as below to return a task:

private async Task<WbsResponse> CallWebService(WbsRequest request)
{
    //Call web service
}
Rey
  • 3,663
  • 3
  • 32
  • 55