How to 'await' a WebClient.UploadStringAsync request? - c#

The async pattern seems to be call an asynchronous, then yield control after awaiting a result, which makes a lot of sense.
However the WebClient class UploadStringAsync method does not return a Task, instead it return void and so cannot be awaited. Instead an event handler can be defined. e.g.
public async Task FlushQueue() {
attempt = 0;
WebClient wc = new WebClient();
while ((queue.Count > 0) && (attempt < ALLOWED_ATTEMPTS)) {
// Copy 10 items from queue and put into buffer ...
...
wc.UploadStringCompleted += (s, e) => {
// if response 200
// Remove 10 sent items from queue
// else attempt++
};
wc.UploadStringAsync("http://example.com/blah", "POST", buffer);
// In an ideal world we could call UploadStringAsync like,
// var response = await wc.UploadStringAsync("http://example.com/blah", "POST", buffer);
}
}
However, this does not await a response and instead quickly rattles through lauching the maximum number of web requests.
Is there a way to yield flow back outside of FlushQueue until the event handler callback is executed?
Edit: This is for a Windows Phone 7.5 project.

You need to use UploadStringTaskAsync, which returns a Task<string>.
The async suffixes on pre-4.5 WebClient methods are unfortunate as they don't match the TAP signatures you'd expect. In general, in that situation it's recommended that API designers use TaskAsync instead of a Async as a suffix - which is exactly what WebClient did... hence DownloadStringTaskAsync etc.
You might also want to consider using HttpClient instead of WebClient.

Related

C# Async and Sync functions confuse me

I have an app in which a button starts creating XMLs. In the end of each XML creation, the SendInvoice function sends it, receives the response and a function (ParseResponse) parses the responses and does the database operations needed.
The idea is that when all the XMLs are created and sent, the application must close.
The problem is that I have lost control with async and the application seems to close before it actually finishes all the jobs. Also XMLs are sent before the previous have been processed.
The ParseResponse function is not asynchronous.
Here is the SendInvoice function.
Can you suggest any good practise?
Thank you in advance.
public async void SendInvoice(string body)
{
Cursor.Current = Cursors.WaitCursor;
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
var uri = "https://xxxx.xxx/SendInvoices?" + queryString;
HttpResponseMessage response;
// Request body
byte[] byteData = Encoding.UTF8.GetBytes(body);
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
response = await client.PostAsync(uri, content);
string responsebody = await response.Content.ReadAsStringAsync();
ParseResponse(response.ToString());
ParseResponse(responsebody);
}
}
The rest of the code
private void button1_Click(object sender, EventArgs e)
{
For
{
......
SendInvoice(xml)
}
System.Windows.Forms.Application.Exit();
}
Since you are calling the method from an Event Handler, this is a case where async void is acceptable, change your Button Click handler method signature to use async, I also added some ConfigureAwait(false) to async method calls - best-practice-to-call-configureawait-for-all-server-side-code and why-is-writing-configureawaitfalse-on-every-line-with-await-always-recommended:
private async void button1_Click(object sender, EventArgs e)
{
//since you are using a for-loop, I'd suggest adding each Task
//to a List and awaiting all Tasks to complete using .WhenAll()
var tasks = new List<Task>();
FOR
{
......
//await SendInvoice(xml).ConfigureAwait(false);
tasks.Add(SendInvoice(xml));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
System.Windows.Forms.Application.Exit();
}
AND change your SendInvoice method signature to return a Task
public async Task SendInvoice(string body)
{
Cursor.Current = Cursors.WaitCursor;
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
var uri = "https://xxxx.xxx/SendInvoices?" + queryString;
HttpResponseMessage response;
// Request body
byte[] byteData = Encoding.UTF8.GetBytes(body);
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
response = await client.PostAsync(uri, content).ConfigureAwait(false);
string responsebody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
ParseResponse(response.ToString());
ParseResponse(responsebody);
}
}
I was very used to multithreaded programming and it took me some time to understand asynchronous programming, because it really has nothing to do with multithreading. It is about doing more with a single thread, or a small number of threads.
asynchronous code is beneficial when the CPU would otherwise be waiting for something besides processing. Examples are: waiting for a network response, waiting for data to be read from disk, waiting on a separate process such as a database server.
It provides a way for the thread you are running to do other things while you wait. C# does this using Task. A task is some work that is being done, and it can be running or it can be waiting, and when waiting it doesn't need a thread attached.
All asynchronous functions must return a Task to be useful. So your function should be:
public async Task SendInvoice() {
...
The async keyword is used by the compiler to automatically wrap your function in a task object so you don't need to worry about a lot of the details. You just use await when calling another async function. You could do more work yourself to create tasks or return a task from another async function, or even call multiple async functions and await all of them together.
If your async method returns a value, use the generic Task: Task<String>, for example.
The Task is returned from an async method before the task completes. That is what allows the thread to be used by something else, but it has to get back to that starting place, which is why asynchonous programming you'll hear "async all the way up". It doesn't really do any good until it gets back to a caller that has multiple tasks to balance, which is usually the entry point of your application or the web request.
You can make your C# Main method async, but it mostly won't matter unless your process is really doing multiple things at the same time. For a web application, that can just be handling multiple requests. For a standalone app, it means you can query multiple APIs, make multiple web requests or db queries at the same time, and await them all, just using a single thread. Obviously, that can make things faster (at least locally, the external resources may have more work to do).
For a simple way to keep your program from exiting, if you have an asynchronous main, just await the call to SendInvoice. If your main is not async, you can use something like:
SendInvoice().Wait()
or
SendInvoice().Result
Using Wait() or Result will lock the thread until the task completes. It typically will make that thread exclusively available to the task so the thread cannot be used for any other tasks. If there are more threads in the threadpool, other tasks may continue to run, but typically using Wait/Result on a single Task defeats the point of asynchronous programming, so keep that in mind.
EDIT
Now that you have posted your calling code, it appears your call is in a loop. This is a good opportunity to take advantage of async calls and send ALL the invoices at once.
private async void button1_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
FOR
{
......
t = SendInvoice(xml).ConfigureAwait(false);
tasks.Add(t)
}
await Task.WhenAll(tasks).ConfigureAwait(false);
System.Windows.Forms.Application.Exit();
}
That will send ALL the invoices, then return from the handler, and then exit once all the responses have been received.

Async/await, run blocking the UI without obvious reason

I have been reasearching once again the async tasks. No mater how i set the Tasks, my application suffers from UI freeze all the time. I have the following code for downloading the string from a webpage:
internal string DownloadString(string URL)
{
var result = LoadCompanyContracts(URL);
return result.Result;
}
internal async Task<string> LoadCompanyContracts(string URL)
{
Task<string> task2 = Task<string>.Factory.StartNew(() =>
{
for (int i = 0; i <= 10000000; i++) Console.WriteLine(i);
WebClient wc = new WebClient();
string tmp = wc.DownloadString(new Uri(URL));
return tmp;
});
return task2.Result;
}
When i execute this task and during the for loop the UI of my application is freezing. Even though i believe that this code should not freeze the UI i am not able to find a solution. I have tried many different options and really want to use tasks instead of threads or events with webclient async.
Info: I am using .net 4.5 for my project. The difference in my code is that these functions are inside a class library(don't know if it matters).
Is it possible to run this code without blocking the user interface with async await by calling the DownloadString function from my code? If not what are the alternatives(any good nuget packages)?
The async keyword doesn't make something run asynchronously, it enables you to use await to await an already asynchronous operation. You need to use
DownloadStringTaskAsync to truly download in an asynchronous manner:
internal async Task<string> LoadCompanyContracts(string URL)
{
....
using(var wc = new WebClient())
{
string tmp = await wc.DownloadStringTaskAsync(new Uri(URL));
return tmp;
}
}
await by itself returns execution in the original execution context (ie the UI thread). This may or may not be desirable, which is why library code typically uses ConfigureAwait(false); and lets the final user of the library to decide how to await:
string tmp = await wc.DownloadStringTaskAsync(new Uri(URL))
.ConfigureAwait(false);
Finally, there's no point in awaiting if you are going to call .Result from the top-level function. There is no point in using await at all if you don't want to do use the method's result in your code. LoadCompanyContracts could be just:
internal Task<string> LoadCompanyContracts(string URL)
{
....
using(var wc = new WebClient())
{
return wc.DownloadStringTaskAsync(new Uri(URL))
.ConfigureAwait(false);
}
}
Oops
Typically, you don't need to use await at all if you just return the result of an asynchronous operation. The method could just return wc.DownloadStringTaskAsync(..); BUT that would cause the method to return and dispose the WebClient before download finishes. Avoiding the using block isn't a solution either, as it will let an expensive object like WebClient alive longer than necessary.
That's why HttpClient is preferable to WebClient: a single instance supports multiple concurrent calls, which means you can have just one instance eg as a field and reuse it, eg:
HttpClient _myClient =new HttpClient();
internal Task<string> LoadCompanyContractsAsync(string URL)
{
....
return _myClient.GetStringAsync(new Uri(URL))
.ConfigureAwait(false);
}
}
You could get rid of your DownloadString since it doesn't do anything on top of LoadCompanyContracts. If it does use the result of LoadCompanyContracts, it should be rewritten as:
internal async Task<string> DownloadString(string URL)
{
var result = await LoadCompanyContracts(URL);
//Do something with the result
return result;
}
EDIT
The original answer used DownloadStringAsync which is a legacy method that raises an event when download completes. The correct method is DownloadStringTaskAsync
EDIT 2
Since we are talking about a UI, the code can be made asynchronous all the way to the top event handler by using the async void syntax for the handler, eg async void Button1_Click, eg:
async void LoadCustomers_Click(...)
{
var contracts=await LoaCompanyContracts(_companyUrls);
txtContracts>Text=contracts;
}
In this case we want to return to the original thread, so we don't use ConfigureAwait(false);

Wait for another method to invoke and then continue with result

I am trying to invoke a method from another .dll file .
It is sending a message through the VPN then Return the RecievedMessage from another computer.
As you now it takes time to sending and receiving message and VpnObject just send message and I should wait for listener to invoke the RecievedMessage.
This method is like this!
public string RecievedMessage()
{
string Recieved ;
// Some VPN Code and then return the result;
return Recieved;
}
public string SendAndRecieveMessage(string MessageToSend)
{
string RecievedAnswer = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
//Then want to Recieve the answer and return the answer here .
return RecievedAnswer;
}
I'm just thinking how can wait for RecievedMessage to invoke then return the result .
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
Is there anyway to continue from SendAndRecieveMessage just when RecievedMessage invoked ? (I think it is something with async and await but don't know how!)
Edit :VpnObject is just a sender and receiver through the vpn . it contains a simple socket send and a listener that invoke a method(RecievedMessage) when new message received .
Whether or not you have an alternative to polling depends on whether the library you are using provides any events or callbacks that will tell you when the request has completed.
Either way, the standard approach to exposing the deferred result of an asynchronous operation is to use a Task. Your method signature would look like this:
public Task<string> SendAndRecieveMessage(string MessageToSend)
Now, how you actually implement the method depends on what API VpnObject exposes. TaskCompletionSource is very useful for this kind of thing.
If VpnObject has an event that fires when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived += (s, e) => tcs.SetResult(e.Message);
...
return tcs.Task;
}
If VpnObject can accept a callback that it will invoke when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived(message => tcs.SetResult(message));
...
return tcs.Task;
}
If VpnObject doesn't support any of this, you can fall back to polling:
public async Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
while(!VpnObject.IsMessageReceived)
await Task.Delay(500); // Adjust to a reasonable polling interval
...
return VpnObject.Message;
}
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
A spin while loop is definitely not the way to implement this. Even with a sleep, it's clunky, and C# has multiple ways to solve this problem.
It's not entirely clear how your VPN Send and Receive method works, but the idea for solving this is to either use a callback approach, or as you noted, use C# async framework.
Without more details on the VPN Object, I'll just have to have some stub methods. The idea is to create a Task that returns the string, mark it as an async task, then await for it to complete. In your case, the task is receiving the VPN response string.
Something like this.
public Task<string> ReceivedMessage()
{
//get the response from the VPN Object.
string Received = VpnObject.GetResponse();
var ts = new TaskCompletionSource<string>();
ts.SetResult(Received);
// Some VPN Code and then return the result;
return ts.Task;
}
public async Task<string> SendAndReceiveMessageAsync(string MessageToSend)
{
string result = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
result = await ReceivedMessage();
return result;
}

WebClient Timeout takes longer than expected (Using: Rx, Try Catch, Task)

Problem: I inherited WebClient in ExtendedWebClient where I override the WebRequest's timeout property in the GetWebRequest method. If I set it to 100ms, or even 20ms, it always takes up to more than 30 seconds at least. Sometimes it seems to not get through at all.
Also, when the service (see code below) serving the images comes back online again, the code written in Rx / System.Reactive does not push images into the pictureBox anymore?
How can I get around this, what am I doing wrong? (See code below)
Test case: I have a WinForms test project set up for this, which is doing the following.
GetNextImageAsync
public async Task<Image> GetNextImageAsync()
{
Image image = default(Image);
try {
using (var webClient = new ExtendedWebClient()) {
var data = await webClient.DownloadDataTaskAsync(new Uri("http://SOMEIPADDRESS/x/y/GetJpegImage.cgi"));
image = ByteArrayToImage(data);
return image;
}
} catch {
return image;
}
}
ExtendedWebClient
private class ExtendedWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
var webRequest = base.GetWebRequest(address);
webRequest.Timeout = 100;
return webRequest;
}
}
3.1 Presentation code (using Rx)
Note: It has actually never reached the "else" statement in the pictures.Subscribe() body.
var pictures = Observable
.FromAsync<Image>(GetNextImageAsync)
.Throttle(TimeSpan.FromSeconds(.5))
.Repeat()
;
pictures.Subscribe(img => {
if (img != null) {
pictureBox1.Image = img;
} else {
if (pictureBox1.Created && this.Created) {
using (var g = pictureBox1.CreateGraphics()) {
g.DrawString("[-]", new Font("Verdana", 8), Brushes.Red, new PointF(8, 8));
}
}
}
});
3.2 Presentation code (using Task.Run)
Note 1: Here the "else" body is getting called, though WebClient still takes longer than expected to timeout....
Note 2: I don't want to use this method, because this way I can't "Throttle" the image stream, I'm not able to get them in proper order, and do other stuff with my stream of images... But this is just example code of it working...
Task.Run(() => {
while (true) {
GetNextImageAsync().ContinueWith(img => {
if(img.Result != null) {
pictureBox1.Image = img.Result;
} else {
if (pictureBox1.Created && this.Created) {
using (var g = pictureBox1.CreateGraphics()) {
g.DrawString("[-]", new Font("Verdana", 8), Brushes.Red, new PointF(8, 8));
}
}
}
});
}
});
As reference, the code to tranfser the byte[] to the Image object.
public Image ByteArrayToImage(byte[] byteArrayIn)
{
using(var memoryStream = new MemoryStream(byteArrayIn)){
Image returnImage = Image.FromStream(memoryStream);
return returnImage;
}
}
The Other Problem...
I'll address cancellation below, but there is also a misunderstanding of the behaviour of the following code, which is going to cause issues regardless of the cancellation problem:
var pictures = Observable
.FromAsync<Image>(GetNextImageAsync)
.Throttle(TimeSpan.FromSeconds(.5))
.Repeat()
You probably think that the Throttle here will limit the invocation rate of GetNextImageAsync. Sadly that is not the case. Consider the following code:
var xs = Observable.Return(1)
.Throttle(TimeSpan.FromSeconds(5));
How long do you think it will take the OnNext(1) to be invoked on a subscriber? If you thought 5 seconds, you'd be wrong. Since Observable.Return sends an OnCompleted immediately after its OnNext(1) the Throttle deduces that there are no more events that could possibly throttle the OnNext(1) and it emits it immediately.
Contrast with this code where we create a non-terminating stream:
var xs = Observable.Never<int>().StartWith(1)
.Throttle(TimeSpan.FromSeconds(5));
Now the OnNext(1) arrives after 5 seconds.
The upshot of all this is that your indefinite Repeat is going to batter your code, requesting images as fast as they arrive - how exactly this is causing the effects you are witnessing would take further analysis.
There are several constructions to limit the rate of querying, depending on your requirements. One is to simply append an empty delay to your result:
var xs = Observable.FromAsync(GetValueAsync)
.Concat(Observable.Never<int>()
.Timeout(TimeSpan.FromSeconds(5),
Observable.Empty<int>()))
.Repeat();
Here you would replace int with the type returned by GetValueAsync.
Cancellation
As #StephenCleary observed, setting the Timeout property of WebRequest will only work on synchronous requests. Having looked at this necessary changes to implement cancellation cleanly with WebClient, I have to concur it's such a faff with WebClient you are far better off converting to HttpClient if at all possible.
Sadly, even then the "easy" methods to pull back data such as GetByteArrayAsync don't (for some bizarre reason) have an overload accepting a CancellationToken.
If you do use HttpClient then one option for timeout handling is via the Rx like this:
void Main()
{
Observable.FromAsync(GetNextImageAsync)
.Timeout(TimeSpan.FromSeconds(1), Observable.Empty<byte[]>())
.Subscribe(Console.WriteLine);
}
public async Task<byte[]> GetNextImageAsync(CancellationToken ct)
{
using(var wc = new HttpClient())
{
var response = await wc.GetAsync(new Uri("http://www.google.com"),
HttpCompletionOption.ResponseContentRead, ct);
return await response.Content.ReadAsByteArrayAsync();
}
}
Here I have used the Timeout operator to cause an empty stream to be emitted in the event of timeout - other options are available depending on what you need.
When Timeout does timeout it will cancel it's subscription to FromAsync which in turn will cancel the cancellation token it passes indirectly to HttpClient.GetAsync via GetNextImageAsync.
You could use a similar construction to call Abort on a WebRequest too, but as I said, it's a lot more of a faff given there's no direct support for cancellation tokens.
To quote the MSDN docs:
The Timeout property affects only synchronous requests made with the GetResponse method. To time out asynchronous requests, use the Abort method.
You could mess around with the Abort method, but it's easier to convert from WebClient to HttpClient, which was designed with asynchronous operations in mind.

Changing from Synchronous mindset to Asynchronous

I'm busy with a windows phone application that of course uses silverlight. This means that calling any webservices has to be done asynchronously, and since this is all good and well in regards to best practice in preventing your entire app in hanging when waiting for a resource, I'm still stuck in the "synchronous mindset"...
Because the way I see it now is that you end up having 2 methods that needs to handle one function, e.g:
1)The method that actually calls the webservice:
public void myAsyncWebService(DownloadStringCompletedEventHandler callback)
{
//Url to webservice
string servletUrl = "https://deangrobler.com/someService/etc/etc"
//Calls Servlet
WebClient client = new WebClient();
client.DownloadStringCompleted += callback;
client.DownloadStringAsync(new Uri(servletUrl, UriKind.Absolute));
}
2) and the method that handles the data when it eventually comes back:
private void serviceReturn(object sender, DownloadStringCompletedEventArgs e)
{
var jsonResponse = e.Result;
//and so on and so forth...
}
So instead of having to just create and call a single method that goes to the webservice, gets the returned result and sent it back to me like this:
public string mySyncWebService(){
//Calls the webservice
// ...waits for return
//And returns result
}
I have to in a Class call myAsyncWebService, AND create another method in the calling class that will handle the result returned by myAsyncWebService. Just, in my opinion, creates messy code. With synchronous calls you could just call one method and be done with it.
Am I just using Asynchronous calls wrong? Is my understanding wrong? I need some enlightment here, I hate doing this messy-async calls. It makes my code too complex and readability just goes to... hell.
Thanks for anyone willing to shift my mind!
You have to turn your mind inside out to program asynchronously. I speak from experience. :)
Am I just using Asynchronous calls wrong? Is my understanding wrong?
No. Asynchronous code is fairly difficult to write (don't forget error handling) and extremely difficult to maintain.
This is the reason that async and await were invented.
If you're able to upgrade to VS2012, then you can use Microsoft.Bcl.Async (currently in beta) to write your code like this:
string url1 = "https://deangrobler.com/someService/etc/etc";
string jsonResponse1 = await new WebClient().DownloadStringTaskAsync(url1);
string url2 = GetUriFromJson(jsonResponse1);
string jsonResponse2 = await new WebClient().DownloadStringTaskAsync(url2);
Easy to write. Easy to maintain.
Async is like when you make a telephone call and get an answering machine, if you want a return call you leave your number. The first method is your call asking for data, the second is the "number" you've left for the return call.
It all becomes much easier and readable if you use lambdas instead. This also enables you to access variables declared in the "parent" method, like in the following example:
private void CallWebService()
{
//Defined outside the callback
var someFlag = true;
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
//Using lambdas, we can access variables defined outside the callback
if (someFlag)
{
//Do stuff with the result.
}
};
client.DownloadStringAsync(new Uri("http://www.microsoft.com/"));
}
EDIT: Here is another example with two chained service calls. It still isn't very pretty, but imho it is a little more readable than the OPs original code.
private void CallTwoWebServices()
{
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
//1st call completed. Now make 2nd call.
var client2 = new WebClient();
client2.DownloadStringCompleted += (s2, e2) =>
{
//Both calls completed.
};
client2.DownloadStringAsync(new Uri("http://www.google.com/"));
};
client.DownloadStringAsync(new Uri("http://www.microsoft.com/"));
}
To avoid creating messy code, if you can't use the async / await pattern because you are on older framework, you will find helpful check CoRoutines in their Caliburn Micro implemantation. With this pattern you create an enumerable yielding at each turn a new asynchronous segment to execute: by the reader point of view asynchronous steps appear as a sequence, but walking among the steps ( so yielding the next one ) is done externally by asynchronously wait the single task. It is a nice pattern easy to implement and really clear to read.
BTW if you don't want to use Caliburn Micro as your MVVM tool because you are using something else, you can use just the coroutine facility, it is very insulated inside the framework.
Let me just post some code from an example in this blog post.
public IEnumerable<IResult> Login(string username, string password)
{
_credential.Username = username;
_credential.Password = password;
var result = new Result();
var request = new GetUserSettings(username);
yield return new ProcessQuery(request, result, "Logging In...");
if (result.HasErrors)
{
yield return new ShowMessageBox("The username or password provided is incorrect.", "Access Denied");
yield break;
}
var response = result.GetResponse(request);
if(response.Permissions == null || response.Permissions.Count < 1)
{
yield return new ShowMessageBox("You do not have permission to access the dashboard.", "Access Denied");
yield break;
}
_context.Permissions = response.Permissions;
yield return new OpenWith<IShell, IDashboard>();
}
Isn't it easy to read? But it is is actually asynchronous: each yield steps are executed in an asynchronous manner and the execution flow again after the yield statement as soon the previous task completed.
With synchronous calls you could just call one method and be done with it.
Sure, but if you do that from the UI thread you will block the entire UI. That is unacceptable in any modern application, in particular in Silverlight applications running in the browser or in the phone. A phone that is unresponsive for 30 seconds while a DNS lookup times out is not something anybody wants to use.
So on the UI thread, probably because the user did some action in the UI, you start an asynchronous call. When the call completes a method is called on a background thread to handle the result of the call. This method will most likely update the UI with the result of the asynchronous call.
With the introduction of async and await in .NET 4.5 some of this "split" code can be simplified. Luckily async and await is now available for Windows Phone 7.5 in a beta version using the NuGet package Microsoft.Bcl.Async.
Here is a small (and somewhat silly) example demonstrating how you can chain two web service calls using async. This works with .NET 4.5 but using the NuGet package linked above you should be able to do something similar on Windows Phone 7.5.
async Task<String> GetCurrencyCode() {
using (var webClient = new WebClient()) {
var xml = await webClient.DownloadStringTaskAsync("http://freegeoip.net/xml/");
var xElement = XElement.Parse(xml);
var countryName = (String) xElement.Element("CountryName");
return await GetCurrencyCodeForCountry(countryName);
}
}
async Task<String> GetCurrencyCodeForCountry(String countryName) {
using (var webClient = new WebClient()) {
var outerXml = await webClient.DownloadStringTaskAsync("http://www.webservicex.net/country.asmx/GetCurrencyByCountry?CountryName=" + countryName);
var outerXElement = XElement.Parse(outerXml);
var innerXml = (String) outerXElement;
var innerXElement = XElement.Parse(innerXml);
var currencyCode = (String) innerXElement.Element("Table").Element("CurrencyCode");
return currencyCode;
}
}
However, you still need to bridge between the UI thread and the async GetCurrencyCode. You can't await in an event handler but you can use Task.ContinueWith on the task returned by the async call:
void OnUserAction() {
GetCurrencyCode().ContinueWith(GetCurrencyCodeCallback);
}
void GetCurrencyCodeCallback(Task<String> task) {
if (!task.IsFaulted)
Console.WriteLine(task.Result);
else
Console.WriteLine(task.Exception);
}

Categories

Resources