Windows Phone 7: web service problem
From a WindowsPhone7 application I need to query a web service by sending a "category" string parameter an expecting to get a string in return. I tried to follow identically the "Weather Forecast" sample from the MSDN, but I always get en empty string. If I add some Debug.WriteLine commands I can see that the callback executes AFTER the response is returned, that is: the return doesn't wait for the asynchronous operation to end.
IMHO I respected the sample code at 100%, can anyone see where things go wrong? The code is below, thank you for your time:
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
/// <summary>
/// Event handler to handle when this page is navigated to
/// </summary>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
SearchService searchService = new SearchService();
searchService.GetCategoryCounter("pizza")
Globals.pizzaCounter = searchService.Counter;
searchService.GetCategoryCounter("pasta")
Globals.pastaCounter = searchService.Counter;
pizzacounter.Text = Globals.pizzaCounter;
pastacounter.Text = Globals.pastaCounter;
}
}
.
public class SearchService
{
#region member variables
private string currentCategoryCount = "";
public event PropertyChangedEventHandler PropertyChanged;
#endregion member variables
#region accessors
public String Counter
{
get
{
return currentCategoryCount;
}
set
{
if (value != currentCategoryCount)
{
currentCategoryCount = value;
NotifyPropertyChanged("Counter");
}
}
}
#endregion accessors
#region private Helpers
/// <summary>
/// Raise the PropertyChanged event and pass along the property that changed
/// </summary>
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
#endregion private Helpers
/*
* GetCategoryCounter Method:
*
* @param category
* : the category whose counter is to be retrieved
*/
public void GetCategoryCounter(string category)
{
try
{
// form the URI
UriBuilder fullUri = new UriBuilder("http://www.mywebsite.com/items/stats");
fullUri.Query = "category=" + category;
// initialize a new WebRequest
HttpWebRequest counterRequest = (HttpWebRequest)WebRequest.Create(fullUri.Uri);
// set up the state object for the async request
CounterUpdateState counterState = new CounterUpdateState();
counterState.AsyncRequest = counterRequest;
// start the asynchronous request
counterRequest.BeginGetResponse(new AsyncCallback(HandleCounterResponse), counterState);
return;
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// Handle the information returned from the async request
/// </summary>
/// <param name="asyncResult"></param>
private void HandleCounterResponse(IAsyncResult asyncResult)
{
// get the state information
CounterUpdateState counterState = (CounterUpdateState)asyncResult.AsyncState;
HttpWebRequest counterRequest = (HttpWebRequest)counterState.AsyncRequest;
// end the async request
counterState.AsyncResponse = (HttpWebResponse)counterRequest.EndGetResponse(asyncResult);
Stream streamResult;
string currentCount = "";
try
{
// get the stream containing the response from the async call
streamResult = counterState.AsyncResponse.GetResponseStream();
StreamReader reader = new StreamReader(streamResult);
currentCount = reader.ReadToEnd();
// copy the data over
Deployment.Current.Dispatcher.BeginInvoke(() 开发者_开发问答=>
{
Counter = currentCount;
});
}
catch (FormatException ex)
{
throw ex;
}
}
}
/// <summary>
/// State information for our BeginGetResponse async call
/// </summary>
public class CounterUpdateState
{
public HttpWebRequest AsyncRequest { get; set; }
public HttpWebResponse AsyncResponse { get; set; }
}
Your sample code wouldn't even compile... compare this:
Globals.pizzaCounter = searchService.GetCategoryCounter("pizza");
with this:
public void GetCategoryCounter(string category)
Note how it's a void method - so you can't assign the return value to Globals.pizzaCounter
.
You need to bear in mind that this happens asynchronously. As you said, "If I add some Debug.WriteLine commands I can see that the callback executes AFTER the response is returned, that is: the return doesn't wait for the asynchronous operation to end." That's the whole meaning of it being asynchronous. You don't want to block your UI thread waiting for the service to return.
Instead, you should pass your service fetching code a callback to execute when your service has returned some data. That will then need to marshal back to the UI thread via the Dispatcher
, and then it can update the textbox. Your code already does some of this, by marshalling back to the UI thread and then updating the property, which will raise the PropertyChanged
event, letting the UI know to refetch the data from the property. However, it looks like there's only one counter, whereas you're trying to fetch two different categories. You should probably separate out the service itself from a "category counter" (or whatever) which knows about the search service, but also is specific to one category. You'd then have two instances of this (but only one search service); each textbox would bind to the relevant instance of the category counter.
精彩评论