I have a method as follows:
public decimal GetExchangeRate(string fromCurrency, string toCurrency)
{
GoogleCurrencyService googleCurrencyService = new GoogleCurrencyService();
return googleCurrencyService.GetRateForCurrency(fromCurrency, toCurrency);
}
and another class as follows
public class GoogleCurrencyService
{
public decimal GetRateForCurrency(string fromCurrency, string toCurrency)
{
try
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(StringDownloadCompleted);
client.DownloadStringAsync(new Uri(_requestUri + fromCurrency + "=?" + toCurrency));
}
catch (Exception)
{
ExchangeRate = 0;
}
return ExchangeRate;
}
private void StringDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
_response = e.Result;
ExchangeRate = ParseResponseAndGetExchangeRate();
}
}//class GoogleCurrencyService
the variable ExchangeRate always come out as zero, so I believe the function call "GetRateForCurrency" returns before the async callback gets called. How do I make sure that does not happen as I need the variable ExchangeRate to be set before being returned. Thanks. Also, I have noticed that the callback never gets called as I have a breakpoint in it and the exception as well which does not get called. So I donot know where the problem is.Any help appreciated.
You can use an event wait handle to block the current thread and wait for the async call...
public class GoogleCurrencyService
{
private const string RequestUri = "http://www.google.com/ig/calculator?hl=en&q=1{0}%3D%3F{1}";
public decimal ExchangeRate { get; private set; }
public decimal GetRateForCurrency(string fromCurrency, string toCurrency)
{
ExchangeRate = 0;
// use a signaler to block this thread and wait for the async call.
var signaler = new ManualResetEvent(false);
try
{
var client = new WebClient();
client.DownloadStringCompleted += StringDownloadCompleted;
// pass the signaler as user token
client.DownloadStringAsync(new Uri(String.Format(RequestUri, fromCurrency, toCurrency)), signaler);
// wait for signal, it will be set by StringDownloadCompleted
signaler.WaitOne();
}
finally
{
signaler.Dispose();
}
return ExchangeRate;
}
private void StringDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
try
{
ExchangeRate = ParseResponseAndGetExchangeRate(e.Result);
}
finally
{
// set signal
((ManualResetEvent)e.UserState).Set();
}
}
private decimal ParseResponseAndGetExchangeRate(string result)
{
return 123;
}
}
EDIT: The same class using an async pattern
public class GoogleCurrencyService
{
private const string RequestUri = "http://www.google.com/ig/calculator?hl=en&q=1{0}%3D%3F{1}";
public void GetRateForCurrency(string fromCurrency, string toCurrency, Action<decimal> callback)
{
var client = new WebClient();
client.DownloadStringCompleted += StringDownloadCompleted;
// pass the callback as user token
client.DownloadStringAsync(new Uri(String.Format(RequestUri, fromCurrency, toCurrency)), callback);
}
private void StringDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
// parse response to get the rate value
var rate = ParseResponseAndGetExchangeRate(e.Result);
// if a callback was specified, call it passing the rate.
var callback = (Action<decimal>)e.UserState;
if (callback != null)
callback(rate);
}
private decimal ParseResponseAndGetExchangeRate(string result)
{
return 123;
}
}
Consuming the async class:
// this is your UI form/control/whatever
public class MyUI
{
public void OnButtonToGetRateClick()
{
var from = "USD"; // or read from textbox...
var to = "EUR";
// call the rate service
var service = new GoogleCurrencyService();
service.GetRateForCurrency(from, to, (rate) =>
{
// do stuff here to update UI.
// like update ui.
});
}
}
Maybe you'll have to dispatch the UI changes to ui thread. I not have WP framework here to confirm that this is the case, but I think it is.
when you run an async method, you will get result in completed method that is
StringDownloadCompleted
so in your code you call async method and return immediatly ExchangeRate which will always be 0.
you have to get ExchangeRate in your completed method StringDownloadCompleted
if you want to get ExchangeRate in your GetRateForCurrency make a synchronous call
client.DownloadString(new Uri(_requestUri + fromCurrency + "=?" + toCurrency));
here's what you need to do.
you create an eventin the class. in your code, you fire the async web client call. Once the call is complete, you wrap the data and set the event. I tend to define event args which can hold the data.
once you set the event the caller will get notified.
if you want an example, have a look at the source in my post here http://invokeit.wordpress.com/2012/06/30/bing-mapcontrol-offline-tiles-solution-wpdev-wp7dev/
its an extension of bing maps sample and it contains address finder class. have a look at how it is fired and how client gets notified
ok wait after call uri
public decimal GetRateForCurrency(string fromCurrency, string toCurrency) {
try
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(StringDownloadCompleted);
client.DownloadStringAsync(new Uri(_requestUri + fromCurrency + "=?" + toCurrency));
Thread.sleep(500000); //waiting
}
catch (Exception)
{
ExchangeRate = 0;
}
return ExchangeRate;
}
so set a webcontrol like label and do this
private void StringDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
_response = e.Result;
yourlabel.Text = _response ;
ExchangeRate = ParseResponseAndGetExchangeRate();
}
Your only choice here to make GetRateForCurrency
asynchronous too (which means raising its own Completed
event).
Is the TPL were supported, you could use Task<T>
as a nice way to wrap the asynchony down the chain, but unfortunately it's not supported by WP7.
As an alternative, and what I've done, is to use the Reactive Extensions ( Microsoft.Phone.Reactive
) and pass an IObservable
down the chain - however, Rx is a lot to learn if you're only going ot use it for this one scenario.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.