0

The preamble

We're implementing a MVC2 site that needs to consume an external API via https (We cannot use WCF or even old-style SOAP WebServices, I'm afraid). We're using AsyncController wherever we need to communicate with the API, and everything is running fine so far.

Some scenarios have come up where we need to make multiple API calls in series, using results from one step to perform the next.

The general pattern (simplified for demonstration purposes) so far is as follows:

public class WhateverController : AsyncController
{
    public void DoStuffAsync(DoStuffModel data)
    {
        AsyncManager.OutstandingOperations.Increment();
        var apiUri = API.getCorrectServiceUri();
        var req = new WebClient();
        req.DownloadStringCompleted += (sender, e) =>
        {
            AsyncManager.Parameters["result"] = e.Result;
            AsyncManager.OutstandingOperations.Decrement();
        };
        req.DownloadStringAsync(apiUri);
    }

    public ActionResult DoStuffCompleted(string result)
    {
        return View(result);
    }
}

We have several Actions that need to perform API calls in parallel working just fine already; we just perform multiple requests, and ensure that we increment AsyncManager.OutstandingOperations correctly.

The scenario

To perform multiple API service requests in series, we presently are calling the next step within the event handler for the first request's DownloadStringCompleted. eg,

req.DownloadStringCompleted += (sender, e) =>
{
    AsyncManager.Parameters["step1"] = e.Result;
    OtherActionAsync(e.Result);
    AsyncManager.OutstandingOperations.Decrement();
}

where OtherActionAsync is another action defined in this same controller following the same pattern as defined above.

The question

Can calling other async actions from within the event handler cause a possible race when accessing values within AsyncManager?

I tried looking around MSDN but all of the commentary about AsyncManager.Sync() was regarding the BeginMethod/EndMethod pattern with IAsyncCallback. In that scenario, the documentation warns about potential race conditions.

We don't need to actually call another action within the controller, if that is off-putting to you. The code to build another WebClient and call .DownloadStringAsync() on that could just as easily be placed within the event handler of the first request. I have just shown it like that here to make it slightly easier to read.

Hopefully that makes sense! If not, please leave a comment and I'll attempt to clarify anything you like.

Thanks!

rejj
  • 1,216
  • 7
  • 13
  • Is OtherActionAsync(e.Result) even going to be called asynchronously in this example? Won't it be called synchronously when WebClient finishes downloading the string? – JimmyP Sep 07 '11 at 04:32
  • It will include another call to .DownloadStringAsync(), which will have its own DownloadStringCompleted event handler. – rejj Sep 07 '11 at 04:48
  • Hmm ok I'm still struggling to see where you're race condition is sorry. As long as OutstandingOperations is incremented and decremented properly then all values should be available in DoStuffCompleted – JimmyP Sep 07 '11 at 04:58
  • I'm not sure if the event handlers are guaranteed to execute in the same thread or not, and if they need to call AsyncManager.Sync() or not to ensure they are working on the same .Parameters (and same .OutstandingOperations) – rejj Sep 07 '11 at 05:01
  • From your code example, it is not clear what the OtherActionAsync is doing. Are you incrementing the outstanding operations again when you initiate another request? ASP.NET invokes the ActionCompleted method as soon as the pending async operations becomes 0. – Charles Prakash Dasari Sep 07 '11 at 06:33

1 Answers1

0

It turns out the answer is "No".

(for future reference incase anyone comes across this question via a search)

rejj
  • 1,216
  • 7
  • 13