Wednesday, 29 October 2014

Simpler Silverlight/C# syntax for async web service calls using delegates

So, I've been using Web-Service calls in Silverlight for a number of years now in a bunch of different applications with the cumbersome async BeginGetResponse, callback, EndGetResponse syntax. It's all been working great, I happily have a template for this and can bash any new service integrations pretty quickly and has not been getting in the way.

For a simple Get request they look something like this:


public string server = "123.123.123.123";


public void GetExample()
{
    String url = "http://" + server + "/getexample";

    try
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

        IAsyncResult result = null;

        result = request.BeginGetResponse(GetExampleCallback, request);
    }
    catch (Exception)
    {
    }
}

void GetExampleCallback(IAsyncResult ar)
{ 
    var request = ar.AsyncState as HttpWebRequest;
    var response = request.EndGetResponse(ar) as HttpWebResponse;

    using (var reader = new StreamReader(response.GetResponseStream()))
    {
        string result = reader.ReadToEnd();

        // now do something with this
    }
}

Which is all good and I usually have some additional code here to callback to a delegate which can then do something, such as display the result asynchronously with the usual fun and games of getting this back onto the GUI thread using a BeginInvoke Dispatch.

Great. It then gets a little more complicated when you want to make a post request and have to push the XML parameters in another asynch BeginGetRequestStream function which means you're getting callback after callback. Easy enough, these can be bundled into a class for each WebService function and can use some templating to reduce the effort, but it's still pretty tedious. I've stuck with it because it works, I have a pattern and usually it's not too much trouble and once done means I can focus on the other interesting bits of the application.

Just this week though I needed to make a new little application and having some brain-space to look at this again and thinking Swift closures I thought I'd explore a bit how to get rid of the callbacks explicitly in the calls and see if this could be made into a single function. Sort-of and there are pros and cons.

What I came up with looks like this:

using System.Threading.Tasks;

public string server = "123.123.123.123";


public void GetExample2()
{
    String url = "http://" + server + "/getexample2";


    try
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

        IAsyncResult result = null;
        ManualResetEvent mre = new ManualResetEvent(false);

        result = request.BeginGetResponse((cb) =>
        {
            // Callback
            using (var response = request.EndGetResponse(cb) as HttpWebResponse)
            {
                using (var reader = new StreamReader(response.GetResponseStream()))
                {
                }
            }

            mre.Set();

        }, request);


        mre.WaitOne();
    }
    catch (Exception)
    {
    }
}


In this case the callback has been put in as an anonymous delegate so the code can be written in the same function. Now what is all the ManualResetEvent stuff about? Basically this is to handle the aynchronous nature. If you run this in the debugger you can see the BeginGetResponse call being made and then jumping down to mre.WaitOne() which is the normal thread flow of the operation. You can then see the debugger jump back up to the callback. The mre.Set then sets the flow to continue in the main thread once the callback has finished.

So, pros and cons.

The big pro is that the whole operation is now contained in a single function statement and local variables can be used to return the result. You can either make this synchronous now (as the synchronicity has been put into the call with the ManualResetEvent) or callback via a delegate (recommended) with the result.

The con is that there are seemingly multiple code execution entries in a single statement. Remember all those gotos and horrible code years ago. Well, lots of coding best practice is to make code more readable and the execution paths clearer and more understandable. [See also later note on calling from the GUI thread]

The nub of the question is if this is easier to read and understand and means there will be fewer problems. I kind of think so as once the template/pattern is established it's much easier to put together and therefore for me less prone to errors.

The big advantage now is if you need to do a Post, it looks sort of like this:


public void PostExample()
{
    String url = "http://" + server + "/postexample";


    try
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.AllowReadStreamBuffering = false;
        request.Method = "POST";
        request.ContentType = "text/xml";


        IAsyncResult result = null;
        ManualResetEvent mre = new ManualResetEvent(false);


        result = request.BeginGetRequestStream((ac) =>
        {
            // post request callback
            using (Stream stream = request.EndGetRequestStream(ac))
            {

                StreamWriter writer = new StreamWriter(stream);


                string post = "";

                post += "<?xml version='1.0' encoding='UTF-8'?>";
                post += "<somexml/>";

                writer.Write(post);

                writer.Flush();
                writer.Close();
            }

            mre.Set();

        }, null);

        mre.WaitOne();
        mre = new ManualResetEvent(false);

        string reply = "";

        result = request.BeginGetResponse((cb) =>
        {
            // callback response
            var response = request.EndGetResponse(cb) as HttpWebResponse;

            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                reply = reader.ReadToEnd();
            }

            mre.Set();

        }, request);

        // this needs to stay in for some strange reason
        Thread.Sleep(100);

        mre.WaitOne();
    }
    catch (Exception)
    {
    }
}

Which is pretty great.

Now, the additional funny. I don't like the Sleep at the end, but however I tried to work this out, I could not get things to work and the last wait just waited on for ever, so pragmatically it is working for me but is an ugly little hack.

Second thing to note with this method is that these functions cannot be called from the GUI thread directly as the callback in BeginGetResponse never comes back. That caused me a lot of headaches until I found the result. In the case of my application this is not a problem as all the service calls are running from a separate thread and updating the GUI. Still, it's messy. There are ways around this similar to the Dispatch back the other way, but it's not completely clean.

I did mess around with the new C# async keyword and using the task framework but the code really looked ugly to my eyes. Maybe a little more work on that and another post in the future.

No comments:

Post a Comment