Reputation: 3967
Simple enough, here's my code:
/// <summary>
/// Fetches the JSON string from the URL.
/// </summary>
/// <param name="url">URL to download the JSON from.</param>
/// <returns>JSON formatted string</returns>
private static string GetJsonResponse(string url)
{
var result = DownloadString(url).Result;
System.Diagnostics.Debug.WriteLine("Downloaded from {0} with result = {1}", url, result);
return result;
}
private static async Task<string> DownloadString(string feedUrl)
{
var result = "";
System.Diagnostics.Debug.WriteLine("Downloading from {0}", feedUrl);
using (var client = new HttpClient())
{
result = await client.GetStringAsync(new Uri(feedUrl, UriKind.Absolute));
result.Trim();
System.Diagnostics.Debug.WriteLine("Finished download from {0}", feedUrl);
}
return result;
}
it prints the first string but there's no way to get to second one, and this means that the client.GetStringAsync
doesn't return.
The URL is ok, it loads fine.
What's my problem?
EDIT 2: (Removed as useless now)
EDIT 3:
I'm adding the methods that I'm using to let you know what's the flow of my data.
Page.xaml
<phone:LongListSelector Grid.Row="1" x:Name="UpcomingResultsList" ItemsSource="{Binding UpcomingMovies}" ItemTemplate="{StaticResource MovieListDataTemplate}" Margin="0, 10, 0, 0" />
UpcomingMovies
is bound to this property
public List<MovieViewModel> UpcomingMovies
{
get
{
System.Diagnostics.Debug.WriteLine("UpcomingMovies - Begin");
var apiMovies = _serviceRT.FindUpcomingMoviesList().Result; // We get the movies in RT API's format
var movies = apiMovies.Select(result => new MovieViewModel(result)).ToList();
System.Diagnostics.Debug.WriteLine("UpcomingMovies - End");
return movies;
}
}
the _serviceRT.FindUpcomingMoviesList()
method:
/// <summary>
/// Gets a list of upcoming movies.
/// </summary>
/// <returns>MovieSearchResults</returns>
public async Task<MovieSearchResults> FindUpcomingMoviesList()
{
var url = string.Format(LIST_UPCOMING, ApiKey);
var jsonResponse = await GetJsonResponse(url);
var results = Parser.ParseMovieSearchResults(jsonResponse);
return results;
}
Finally, the GetJsonResponse()
is at the beginning of the question.
Now, having the full data flow, how can I bind this property and still make the download complete?
Upvotes: 0
Views: 1104
Reputation: 456517
I predict that further up in your call stack, you are calling Wait
or Result
on a Task
returned from an async
method. This can easily cause deadlock, as I describe on my blog.
This happens because await
will by default capture a "context" which it uses to resume the async
method. In your example, this is most likely a UI context, which always has exactly one thread (the UI thread). So if you block the UI thread by calling Wait
or Result
, then the async
method cannot re-enter the UI context to complete its execution (and you end up waiting for a Task
that cannot complete).
Edit:
Since you're databinding, I recommend using the NotifyTaskCompletion
type here (which will soon be part of my AsyncEx library):
public MyViewModel()
{
UpcomingMovies = NotifyTaskCompletion.Create(LoadUpcomingMoviesAsync());
}
public INotifyTaskCompletion<List<MovieViewModel>> UpcomingMovies { get; private set; }
private async Task<List<MovieViewModel>> LoadUpcomingMoviesAsync()
{
System.Diagnostics.Debug.WriteLine("UpcomingMovies - Begin");
var apiMovies = await _serviceRT.FindUpcomingMoviesList();
var movies = apiMovies.Select(result => new MovieViewModel(result)).ToList();
System.Diagnostics.Debug.WriteLine("UpcomingMovies - End");
return movies;
}
Then you can safely bind to INotifyTaskCompletion<T>.Result
like this:
{Binding UpcomingMovies.Result}
Note that until you load the movies, the result will be null
so your view will be empty. Also, if there is an error loading the movies, your view will always be empty. I recommend that you handle these situations by databinding to the other INotifyTaskCompletion<T>
properties, e.g.:
{Binding UpcomingMovies.IsCompleted}
, which will start out false
and become true
when the loading completes (either successfully or in error){Binding UpcomingMovies.IsSuccessfullyCompleted}
, which becomes true
only if/when the loading completes successfully{Binding UpcomingMovies.IsFaulted}
, which becomes true
only if/when the loading completes with an error{Binding UpcomingMovies.ErrorMessage}
, which extracts the error message (and is null
if there is no error)Upvotes: 1
Reputation: 5369
HttpClient has limitations according to platform it is called from (like other network-related APIs on Windows Phone if compared with "big windows"). Maybe, this how-to will help: http://blogs.msdn.com/b/bclteam/archive/2013/02/18/portable-httpclient-for-net-framework-and-windows-phone.aspx
HttpClientHandler handler = new HttpClientHandler();
httpClient = new HttpClient(handler);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, resourceAddress);
request.Content = streamContent;
if (handler.SupportsTransferEncodingChunked())
{
request.Headers.TransferEncodingChunked = true;
}
HttpResponseMessage response = await httpClient.SendAsync(request);
Upvotes: 0