So after spending most of the night learning about delegates and asynchronous methods, I started to implement an asynchronous wrapper for the Betfair API. Although I now feel I properly understand Delegates and Callbacks (post a comment if you'd like me to post a very simple explanation), imagine my happiness this morning when I discovered the API did in fact have asynchronous methods for pretty much all of it's services. It was one of those times when you just have to move on, coding lesson learned..
So this is a bit of a restart in the blog - I've started a new Visual Studio project, and am now working exclusively with asynchronous methods, where they are available. But first, an explanation of why working asynchronously is good when we're dealing with web services..
When you call a remote method, there is going to be a delay between when you call it, and when it returns. In a single-threaded synchronous program (which is probably the way you learned to program), the delay means that your program is going to "block", and appear to hang until the function returns. Obviously this is bad from a user interface perspective, and is also bad from a program perspective - if there's other processing your program could be getting on with, why waste processor cycles waiting for your remote function call to return?
An Asynchronous Method is one that lets you know when it is finished - so you can call the method, and the program will continue to execute. Once the method is completed, it notifies the program (via an event), and we can deal with the data in a callback method. A callback method is simply a method that is called "automatically", when the remote method returns. There seems to be a lot of complicated demonstration code on the web, so I'm going to try and keep this as simple as possible - obviously this is specific to the Betfair API as well, which means that we don't have to implement callbacks. In a later post, I'll talk about implementing "both sides" of an asynchronous method, but for now lets look again at the
Login service, but this time call it asynchronously.
Logging In To The Betfair API Asynchronously
Because we're now working asynchronously, we will be subscribing to asynchronous events of the Betfair services, and so we need to make sure there is only one instance of the service - I did this by creating a static class and creating the service there:
public static class Session
{
public static global.BFGlobalService bfGlobalService = new global.BFGlobalService();
}
Nice and simple - we now have an easily accessible service. The login code is pretty much the same as before - the only difference this time is we're calling
loginAsync, instead of
login. So the code:
global.LoginReq loginRequest = new global.LoginReq();
loginRequest.username = USERNAME;
loginRequest.password = PASSWORD;
loginRequest.productId = 82;
Session.bfGlobalService.loginAsync(loginRequest,Guid.NewGuid().ToString());
Obviously, replace
USERNAME and
PASSWORD with your login details - either hardcode them, or get them from your user interface programatically.
The second parameter
Guid.NewGuid().ToString() is supplied to an asychronous method to uniquely identify it. This means we can call it repeatedly, without waiting for previous calls to return. To test this, omit the second parameter, and then repeatedly call a method - an exception will be thrown. The unique identifier means that when the method returns, it knows where to return to, and so doesn't get mixed up with other calls. We don't necessarily need it - it's up to you to decided whether you need multiple calling functionality or not..!
If you implement this, you'll see that when the program reaches the last line, it immediately returns and carries on working as normal. The login function, like all Betfair API calls, returns an APIResponseHeader, inside of which there is the session token we must send with the next API call, as well as error information.
But how do we know when this data is available, and how do we retrieve it? Through event notification...which keeps things quite simple - we just have to make sure we've subscribed to this event. I want my main form to know when the data is available, so I've added the following line of code to my constructor:
Session.bfGlobalService.loginCompleted +=new loginCompletedEventHandler(bfGlobalService_loginCompleted);
This means that when the event is fired, a method called
bfGlobalService_loginCompleted will be called. This method is also on my form, and looks like this:
private void bfGlobalService_loginCompleted(object sender, global.loginCompletedEventArgs e)
{
global.LoginResp loginResponse = e.Result;
Session.SaveAPIResponseHeader(loginResponse.header, (Enum)loginResponse.errorCode);
if ((e.Result.errorCode == LoginErrorEnum.OK && e.Result.header.errorCode == global.APIErrorEnum.OK))
{
this.btnAuthenticate.Text = "Logout";
}
else
{
MessageBox.Show("Login Error");
}
}
The return value of the function (in this case, of type
LoginResp), is returned back to the program in the event arguments - we access it through
e.Result. We then save the API response header, using a function in our static class called
SaveAPIResponseHeader, (more on that in a future post), and then I do some error checking before updating the user interface. Post any questions on unclear code in the comments - I'll cover the use of generics and
Enum to aid our error checking in a future post.
To summarise, working asynchronously with the Betfair API is fairly simple - we:
- Call the asynchronous version of the method.
- Subscribe to the asynchronous event related to the method.
- Retrieve the data from in the event handler.
Not much to it at all!