HttpClient doesn't return in Windows Phone - c#

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?

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)

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);

Related

How we can manage async calls into Parallel.ForEach?

I am new at asynchronous programming and I use the following code to collect data from third-party API and every time I am getting different responses. Am I doing the wrong approach?
Parallel.ForEach(products, item =>{
GetProductsInfo(item);
});
public async Task<Product> GetProductsInfo(Product product)
{
var restClientProduct = new RestClient("URL");
var restRequestProduct = new RestRequest(Method.POST);
var proudctRequestJson = JsonConvert.SerializeObject(new ProudctRequest()
{
product_code = product.product_code,
});
restRequestProduct.AddHeader("cache-control", "no-cache");
restRequestProduct.AddHeader("Content-Length", proudctRequestJson.Count().ToString());
restRequestProduct.AddHeader("Content-Type", "application/json");
restRequestProduct.AddHeader("Accept", "application/json");
restRequestProduct.AddParameter("undefined", proudctRequestJson, ParameterType.RequestBody);
var responseProduct = GetResponseContentAsync(restClientProduct, restRequestProduct).Result;
if (responseProduct.StatusCode == HttpStatusCode.OK)
{
// set values form the responseProduct to the product
}
return product;
}
private Task<IRestResponse> GetResponseContentAsync(RestClient theClient, RestRequest theRequest)
{
var tcs = new TaskCompletionSource<IRestResponse>();
theClient.ExecuteAsync(theRequest, response =>
{
tcs.SetResult(response);
});
return tcs.Task;
}
The parts of your code you have shown us is not running asynchronously. You are calling .Result on GetResponseContentAsync(), which will block the thread until it finishes. That means that by the time Parallel.ForEach completes, all the HTTP requests will have completed.
If you are using await somewhere in that block of code you replaced with
// set values form the responseProduct to the product
then it's possible that the results are not being reported before Parallel.ForEach finishes. That is because Parallel.ForEach does not support asynchronous code, so it will not wait for them to finish.
Let's assume that GetProductsInfo is actually running asynchronously
Then the problem is: Parellel.ForEach is not waiting for my asynchronous operations to finish. There are a couple ways to handle this.
Implement your own ForEachAsync. This has been requested, and will probably be added (to .NET Core at least) eventually. But there is actually a sample implementation in the issue where this was requested:
/// <summary>
/// Executes a foreach asynchronously.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <param name="dop">The degrees of parallelism.</param>
/// <param name="body">The body.</param>
/// <returns></returns>
public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
{
return Task.WhenAll(
from partition in System.Collections.Concurrent.Partitioner.Create(source).GetPartitions(dop)
select Task.Run(async delegate
{
using (partition)
{
while (partition.MoveNext())
await body(partition.Current);
}
}));
}
That is written as an extention method, so you would use it like this:
await products.ForEachAsync(10, GetProductsInfo);
Where 10 is the number of request you would like to run at a time.
You can use something like:
Task.WaitAll(items.Select(i => GetProductsInfo(i));
This will run the requests asynchronously, but block the calling thread until they all finish. Alternatively, you can await them, so it doesn't block the calling thread:
await Task.WhenAll(items.Select(i => GetProductsInfo(i))
However, both these methods will fire off all of the requests at once. If you know you will only ever have a small number, then that's fine. But if you might have a very large number, you could flood the web service. Using Parallel.ForEach, or the implementation of ForEachAsync above will send them in blocks.
If you use any of these methods to await the responses, then you really should await GetResponseContentAsync instead of using .Result:
var responseProduct = await GetResponseContentAsync(restClientProduct, restRequestProduct);
Using async/await is especially important in ASP.NET, where there is a maximum number of threads it can use.

Activity indicator not loading correctly

I am using an activity indicator to show to the user while the code goes away and calls to azure.
The call itself works fine and all is working well but the activity indicator loads for a set period of time then afterwards the same delay that I'm trying to prevent to the user still takes place then the next screen loads.
I should probably clarify that I'm relatively new to Xamarin and a lot of this async/await stuff alludes me.
I have put the API calls in a Task and put an await method called sleep in there that runs for about 4 seconds. This was the only way I could get it to run the activity indicator. But it looks like this is also causing the problem, so to summarise I'm stuck.
I want the calls to Azure to take place while the activity indicator is going then when they return open the next page, so as to prevent page lagging and freezing. It does not look good.
So, this is the method that calls to the APIs:
private async Task HandleSubmitCommand()
{
//if (IsLoading) return;
IsLoading = true;
await Sleep();
if (!string.IsNullOrEmpty(IdentityDriver))
{
_entryFieldType = "oid";
_statusResponse = DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityDriver, App.TenantId.Value.ToString());
IsLoading = false;
}
else
{
_entryFieldType = "rid";
_statusResponse = DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityRoute, App.TenantId.Value.ToString());
}
if (_statusResponse == "True")
{
Application.Current.MainPage = new DriverDashboardView();
}
else
Application.Current.MainPage = new NotFoundView();
}
This is the sleep method:
private Task Sleep()
{
return Task.Delay(4000);
}
This is the Login method that calls to the API:
public string Login(string ID, string IDPayload, string TenantID)
{
//await Sleep();
var BaseURL = App.ConFigURL;
var URI = string.Format(BaseURL, TenantID);
using (var httpClient = new HttpClient(new HttpClientHandler()))
{
httpClient.BaseAddress = new Uri(URI);
var Telemetry = new { typeid = ID , id = IDPayload};
var Payload = JsonConvert.SerializeObject(Telemetry);
var SAS = DependencyService.Get<ISasToken>().CreateToken(URI, "RootManageSharedAccessKey", "#####################################");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", SAS);
var Content = new StringContent(Payload, Encoding.UTF8, "application/json");
var Response = httpClient.PostAsync(URI,Content).Result;
return Response.IsSuccessStatusCode.ToString();
}
}
As I stated the calls to Azure are fine but it doesn't seem to be running asynchronously. It doesn't help that I'm not at all comfortable with async/await.
An ideas?
I want the calls to Azure to take place while the activity indicator is going then when they return open the next page, so as to prevent page lagging and freezing.
The await Sleep(); under your HandleSubmitCommand method is unnecessary. At this point, your login operation would be executed after 4s. Based on your
Login method, you exexute the login operation synchronously via httpClient.PostAsync(URI,Content).Result. You could modify your methods as follows:
public async Task<bool> Login(string ID, string IDPayload, string TenantID)
{
.
.
var Response = await httpClient.PostAsync(URI,Content);
return Response.IsSuccessStatusCode;
}
private async Task HandleSubmitCommand()
{
IsBusy = true;
//call the login operation asynchronously
_statusResponse = await DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityDriver, App.TenantId.Value.ToString());
IsBusy = false;
}
Note: You need to change the definition for your IWebService interface as follows:
public interface IWebService
{
Task<bool> Login(string ID, string IDPayload, string TenantID);
}

How to display a System.Threading.Tasks.Task<double> as a label in a WPF app?

I am trying to display the result of my async Task (Below) method as a label in my WPF app.
However when I use the code
Label.Content = Sentiment("en", "text");
and run my application my labels content appears as "System.Threading.Tasks.Task'1[System.Double]"
Also you are not able to convert a Task into any other type as far as I am aware.
So how must I go about getting my labels content to display the Task from my sentiment method?
Thank you.
public async Task<double> Sentiment(string language, string text)
{
HttpClient _httpClient;
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "My Secret API Key");
var serviceEndpoint = "https://westus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment?";
if (string.IsNullOrEmpty(language) || string.IsNullOrEmpty(text))
{
throw new ArgumentNullException();
}
var request = new TextRequest();
request.Documents.Add(new TextDocument(text, language));
var content = new StringContent(JsonConvert.SerializeObject(request), System.Text.Encoding.UTF8, "application/json");
var result = await _httpClient.PostAsync($"{serviceEndpoint}sentiment", content).ConfigureAwait(false);
var response = JObject.Parse(await result.Content.ReadAsStringAsync());
CatchAndThrow(response);
return response["documents"].Children().First().Value<double>("score");
}
So how must I go about getting my labels content to display the Task from my sentiment method?
First, understand that there are two conflicting desires here:
It takes some time to get the data to display from the endpoint.
The computer must have something to display immediately.
The only way to properly satisfy both of these is to immediately display some kind of placeholder value and start the call to the endpoint, and then when the endpoint responds, update the display with the proper data.
Something like this would work in the simple case:
// Immediately, synchronously display placeholder.
Label.Content = "Loading...";
// Asynchronously update with real data.
Label.Content = await Sentiment("en", "text");
I have tried this and now the labels content doesn't change at all.
Please ensure that your code is async all the way, not blocking on async code.
Since you are using WPF, you can also take a more advanced approach that I describe in my article on asynchronous data binding. I have a type NotifyTask<T> which can be used as a data-bindable wrapper for Task<T>. You'd use it as such:
public class MyViewModel
{
public NotifyTask<string> LabelContent { get; }
ViewModel()
{
LabelContent = NotifyTask.Create(
async () => (await Sentiment("en", "text")).ToString(),
"Loading...");
}
}
and then you'd data-bind Label.Content to LabelContent.Result. There are other properties on NotifyTask<T> as well (IsNotCompleted, IsSuccessfullyCompleted, IsFaulted) that allow you to respond to the task's state changes via data binding. E.g., you can show a busy spinner instead of "Loading..." if you want, or show an error if the call to the endpoint fails.
await the result and convert it to string.
var result = await Sentiment("en", "text");
if(result != null)
Label.Content = result.ToString();

Windows phone 8 RestSharp request. Async/await

I know it has been asked a lot, but my problem is, that my method won't wait for the request to be completet, even though i have implemented a TaskCompletionSource, which should have done the job, but it doesn't.
public DecksViewModel(bool local)
{
DList = new List<Deck>();
if (local)
InitializeLocalDeckList();
else
{
Dereffering();
}
}
public async void Dereffering()
{
var e = await InitilaizeWebDeckList();
List<DeckIn> decksIn = JsonConvert.DeserializeObject<List<DeckIn>>(e);
foreach (DeckIn d in decksIn)
{
Deck dadd = new Deck();
dadd.CardCount = 0;
dadd.Name = d.name;
dadd.PicturePath = d.image;
dadd.InstallDirectory = false;
DList.Add(dadd);
}
DataSource = AlphaKeyGroup<Deck>.CreateGroups(DList, System.Threading.Thread.CurrentThread.CurrentUICulture, (Deck s) => { return s.Name; }, true);
}
public Task<String> InitilaizeWebDeckList()
{
var tcs = new TaskCompletionSource<string>();
var client = new RestClient("blabla.com");
var request = new RestRequest("");
request.AddHeader("Authorization", "Basic blabla");
client.ExecuteAsync(request, response =>
{
test = response.Content;
tcs.SetResult(response.Content);
});
return tcs.Task;
}
So when I call the DecksViewModel constructor, I asyncally try to request the data from a webserver and fill the model.
The point is, that the corresponding view "doesn't wait" for the request to fill the model, so it's displayed empty.
I use the
List<AlphaKeyGroup<Deck>> DataSource
to fill a LongListSelector via DataBinding. But DataSource isn't yet set, when it is binded.
I hope you can help
You're calling an async method without awaiting it inside the constructor. That's why "it doesn't wait" (because it has nothing to wait on).
It's usually a bad idea to call an async method inside the constructor for that reason combined with the fact that constructors can't be async.
You should redesign your solution accordingly. An option is to have an async static method that creates an instance and awaits the procedure:
public static async Task CreateInstance(bool local)
{
var model = new DecksViewModel();
if (local)
{
await InitializeLocalDeckList();
}
else
{
await Dereffering();
}
}
That would allow you to not use async void which should only be used in UI even handlers.
You can read more about other options in Stephen Cleary's blog
You are using async void, which means nobody's gonna wait for that. It's just fire and forget.
I see some misunderstanding in the async keyword here:
Your code will only wait for the result of an async method, if you use await. Otherwise that call will just start the async method, but you don't know when it is actually gonna run.
You cannot use await in constructors though.

Part of the code is non-reachable after using c# await

I'm writing the video converting tool for one web site.
Logic is next
User upload the file
System conver it for user (of course user should not wait while converting)
I have a code:
public async void AddVideo(HotelVideosViewModel model)
{
var sourceFile = FileHandler.SaveFile(model.VideoFile);
var fullFilePath = HttpContext.Current.Server.MapPath(sourceFile);
await Task.Factory.StartNew(() => video.Convert(fullFilePath));
model.FileLocation = sourceFile;
model.IsConverted = true;
this.Add<HotelVideos>(Mapper.Map<HotelVideosViewModel, HotelVideos>(model));
}
This start encoding:
await Task.Factory.StartNew(() => video.Convert(fullFilePath));
But the code after this is never executed... Does somebody has a solution?
PS: under video.Conver(...) is have something like:
public bool Convert(string fileLocation)
{
// do staff
}
--- UPDATE ---
Just got the interesting thing. If i'm going to exchange video.Convert(fullFilePath) to Thread.Sleep(2000) everything start to work. So the issue is in Second method i think.
So i'm adding the next file code (draft now):
using Softpae.Media;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class VideoConvertManager
{
private readonly Job2Convert jobConverter = new Job2Convert();
private readonly MediaServer mediaServer = new MediaServer();
public bool Convert(string fileLocation)
{
this.jobConverter.pszSrcFile = fileLocation;
this.jobConverter.pszDstFile = fileLocation + ".mp4";
this.jobConverter.pszDstFormat = "mp4";
this.jobConverter.pszAudioCodec = null;
this.jobConverter.pszVideoCodec = "h264";
if (this.mediaServer.ConvertFile(this.jobConverter))
{
FileHandler.DeleteFile(fileLocation);
return true;
};
FileHandler.DeleteFile(fileLocation);
return false;
}
}
You need to make AddVideo return Task, and await its result before completing the ASP.NET request.
Update: DO NOT call Wait or other blocking methods on an asynchronous Task! This causes deadlock.
You may find my async intro helpful.
P.S. async doesn't change the way HTTP works. You still only have one response per request. If you want your end user (e.g., browser) to not block, then you'll have to implement some kind of queue system where the browser can start video conversion jobs and be notified of their result (e.g., using SignalR). async won't just magically make this happen - it only works within the context of a single request/response pair.
Adding ConfigureAwait(false) will keep the continuation from being forced on the request thread

Categories