How the Async support in RestSharp can help with Report Generation

Note: This was written a long time ago for the then-current version of RestSharp that had experimental Async support. John and his contributors have updated RestSharp tremendously since then, but by now these samples are outdated and only here for illustrative purposes.

I've been using RestSharp in the past weeks for some backend tools, mainly because REST is easy to implement and I like the Deserialization support that comes with it. One of the reasons I wanted to add async support is because I wanted to write a monitoring application.

I am using Microsoft Chart Controls, and I have a server that accepts REST Requests in the form /reports/health/{entity} to return something like this:

<healthdata>
    <healthindex>73</healthindex>
</healthdata>

I don't know which entities are being pinged at compile time. I can't even put them in a config file, as someone might add or remove entities while the application is still running. So I decided to have an architecture with a Timer, a Request Dispatcher and a Response Handler.

The Timer executes every 5 seconds, gets the current list of entities and calls the Request Dispatcher:

private void timer1_Tick(object sender, EventArgs e)
{
    foreach(string entity in GetEntities()){
        DispatchRequest(entity);
    }
}

The Request Dispatcher creates a new RestRequest and a custom State Object. The state object will be important later, for now lets just note that it contains the entityname and the time the request was sent.

private void DispatchRequest(string entityName)
{
    var rq = new RestRequest("health/{entity}", Method.GET);
    rq.AddParameter("entity", entityName, ParameterType.UrlSegment);

    var state = new ServiceState {Entity = entityName, RequestDateUtc = DateTime.UtcNow};
    _client.BeginExecute<healthdata>(rq, HandleResponse, state);
}

The Request Dispatcher doesn't care what happens afterwards, it just happily fires new requests all the time and tells BeginExecute to call HandleResponse afterwards.

private void HandleResponse(IAsyncResult res)
{
    // Note: This assumes the response is always good. In reality, you want to
    // check response.ResponseStatus and act accordingly
    var result = (AsyncResult) res;
    var caller = (RequestExecuteCaller<healthdata>) result.AsyncDelegate;
    var state = res.AsyncState as ServiceState;
    var response = caller.EndInvoke(res);
    AddDataToChart(state.Entity, response.Data.healthindex, state.RequestDateUtc);
}

Here comes our state object into play. If you look at the XML from the server above, you see that it doesn't echo the Entity Name. As HandleResponse handles all responses, it has to know which response corresponds to which server. Also, async operations can come in a different order than the one executed - I may send requests for entities s1, s2, s3, s4 and receive them back in the order s3, s1, s4, s2. If the timer interval is too small and the server latency is too high, I might even receive a later request for s2 before an earlier one - it's pure anarchy.

That's why I've passed a state object to BeginExecute, as this allowed me to capture the entity name and the exact Date of the request. So all that HandleResponse has to do is to get the state and healthdata and call the function that adds the data to the chart with precise information about the Time, the Entity and it's health.

All without ever locking the UI or without giving the dispatcher or the handler too much work that they shouldn't be doing.


(Excuse the horrible colors, I'm only a developer :))

Comments (2)

ChrisApril 26th, 2012 at 13:24

Hi

Any chance of getting the source for this?

Thanks
Chris

mstumMay 5th, 2012 at 15:58

Hello,

I don't have any sourcecode anymore, but it was essentially the code in the blogpost anyway. AddDataToChart was literally doing that, adding points to the MS Chart control.