Most of the code I've written in .NET to make REST calls have been synchronous. Since Silverlight on Windows Phone only supports Async WebClient and HttpWebRequest calls, I was wondering what a good async pattern is for a Class that exposes methods that make REST calls.
For example, I have an app that needs to do the following.
my class exposes a few methods:
since each method needs to call WebClient using Async calls what I need to do is essentially block calling Login till it returns so that I can call GetAlbums().
What is a good way to go about this in my class that exposes those methods?
You might take a look at the Reactive (Rx) framework extensions:
http://www.leading-edge-dev.de/?p=501
http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html
[edit: ooh - found a good link:] http://rxwiki.wikidot.com/101samples
They provide a way to "sequence" events, acting only upon certain conditions met - for example, say you had a method "AuthenticationResult Authenticate(string user, string pass)"
You could do something like:
var foo = Observable.FromAsyncPattern<string, string, AuthenticationResult>
(client.BeginAuthenticate, client.EndAuthenticate);
var bar = foo("username","password");
var result = bar.First();
Effectively turning an asynchronous method to a synchronous one. You can extend this to include "chaining":
var bar = foo("username", "password")
.Then(authresult => DoSomethingWithResult(authresult));
Neat stuff. :)
It really depends on what you want to do with this information. If for instance you are attempting to display the list of albums/categories etc, one way to model this would be to
There is an obvious problem with cases where you want to wait and not proceed until you receive something back over the network (for instance if you want to keep the login page around until you know that you have successfully authenticated). In this case you could just change the page in the async callback.
You could obviously also do something fancier and have a thread wait for an event set by the async callback. I recommend not have the UI thread do this, since it limits your ability to have things like timeouts, and is generally very messy.
We wrote our client-side service layer with all async function signatures that look like this:
public void MyFunction(
ArtType arg,
Action<ReturnType> success,
Action<FailureType> failure);
The service code does an async call to the web service, and when that returns it calls the success callback if the call was successful, and the failure callback if there was a fault/exception. Then the calling code kinda looks like this:
MyServiceInstance.MyFunction(
blahVar,
returnVal => UIInvoker.Invoke(() =>
{
//some success code here
}),
fault => UIInvoker.Invoke(() =>
{
//some fault handling code here
}));
(UIInvoker is just a utility that dispatches back to the UI from a background thread.)
I put something together that is a bit more fluent.
Restful-Silverlight is a library I've created to help with both Silverlight and WP7.
I have included code below to show how you can use the library to retrieve tweets from Twitter.
Sample Usage of Restful-Silverlight retrieving tweets from Twitter:
//silverlight 4 usage
List<string> tweets = new List<string>();
var baseUri = "http://search.twitter.com/";
//new up asyncdelegation
var restFacilitator = new RestFacilitator();
var restService = new RestService(restFacilitator, baseUri);
var asyncDelegation = new AsyncDelegation(restFacilitator, restService, baseUri);
//tell async delegation to perform an HTTP/GET against a URI and return a dynamic type
asyncDelegation.Get<dynamic>(new { url = "search.json", q = "#haiku" })
//when the HTTP/GET is performed, execute the following lambda against the result set.
.WhenFinished(
result =>
{
textBlockTweets.Text = "";
//the json object returned by twitter contains a enumerable collection called results
tweets = (result.results as IEnumerable).Select(s => s.text as string).ToList();
foreach (string tweet in tweets)
{
textBlockTweets.Text +=
HttpUtility.HtmlDecode(tweet) +
Environment.NewLine +
Environment.NewLine;
}
});
asyncDelegation.Go();
//wp7 usage
var baseUri = "http://search.twitter.com/";
var restFacilitator = new RestFacilitator();
var restService = new RestService(restFacilitator, baseUri);
var asyncDelegation = new AsyncDelegation(restFacilitator, restService, baseUri);
asyncDelegation.Get<Dictionary<string, object>>(new { url = "search.json", q = "#haiku" })
.WhenFinished(
result =>
{
List<string> tweets = new List();
textBlockTweets.Text = "";
foreach (var tweetObject in result["results"].ToDictionaryArray())
{
textBlockTweets.Text +=
HttpUtility.HtmlDecode(tweetObject["text"].ToString()) +
Environment.NewLine +
Environment.NewLine;
}
});
asyncDelegation.Go();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With