While on my way back from the MVP Summit, I was playing around with some of the things I had discovered about VS 2005. I mean, if I was going to drip geekiness for the entire conference, why not extend it to the absolute limit. And what I found was something mildly disturbing.
A pet peeve of mine for a while has been how easy it is to make an asynchronous Web service call. Whether the caller knows it or now, that call actually makes your application multi-threaded. For the simplest case, that’s fine. But when you’re making the call from a Windows form application and need to update the user interface with the results of the Web method call, you are getting into an area of complexity that’s more complicated than you realize. Ask any decent developer what the hardest time of bug to crack is and invariably you’ll get ‘race conditions’, ‘deadlocks’ or just the generic ‘multiple threads’ back as the answer. And you have entered that realm unknowingly.
For VS 2005, Microsoft took my complaint and said ‘hey…if updating the UI is soooooo hard, we should make it easier’. Not what I had in mind, trust me. Ask me about a /nomultithread default compiler switch and you’ll be closer. So now instead of calling BeginHelloWorld and EndHelloWorld, it is possible to call HelloWorldAsync and create an event handler for HelloWorldCompleted. Yes, it’s a familiar model. And, as it turns out, HelloWorldCompleted actually fires on the main UI thread, so that ugly MethodInvoker stuff no longer needs to be understood.
But along with changing this asynchronous model, Microsoft also eliminated the Begin/End versions of the methods. Which means if you need to do something like a fork/join pattern, you can’t. Just so that we’re clear, a fork/join would have previously consisted of:
IAsyncResult ar = ws.BeginHelloWorld(null, null);
// Do some stuff in parallel with the web service call
string result = ws.EndHelloWorld(ar);
Because of how EndHelloWorld works, the main thread will block until the background thread has completed. And if the background thread finishes first, processing will continue normally. This is a very common approach if you’re web page were to fire off an asynchronous call to a Web service and wanted to include the results on a page being returned to the client.
As I was saying, since the Begin/End methods are no longer included in the proxy class, this pattern (at least as I have outlined it) is no longer possible. And to replicate the pattern using the event handler model is non-trivial. So my solution was to take the class generated by WSDL (or by adding a Web reference) and putting back in the Begin/End methods that I need. The basic structure is:
public IAsyncResult BeginHelloWorld(AsyncCallback callback, object asyncState)
return this.BeginInvoke("HelloWorld", new object, callback, asyncState);
public string EndHelloWorld(IAsyncResult ar)
object results = this.EndInvoke(ar);
The second parameter on the BeginInvoke call is an array of objects representing the parameters to HelloWorld, so if you’re doing this for a method that takes parameters, you’ll need to change the dimension of the object array and initialize it appropriately.