Unresponsiveness with async event handlers in WPF in .NET 4.5 - c#

I have created a simple async operation which is being kicked of when the button is clicked. Here is the whole code:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private async void Button_Click_1(object sender, RoutedEventArgs e) {
var htmlString = await DowloadPage("http://example.com");
txtBlock1.Text = htmlString;
}
public async Task<string> DowloadPage(string uri) {
using (WebClient client = new WebClient()) {
var htmlString = await client.DownloadStringTaskAsync(uri);
return htmlString;
}
}
}
Very easy. But when I click the button, I experience unresponsiveness on the UI thread. When I try to move around the window while the page is being downloaded, I am unable to.
Any idea what is going wrong?
Edit:
I tried with HttpClient in .NET 4.5 and it worked out pretty great as expected:
public async Task<string> DowloadPage(string uri) {
using (HttpClient client = new HttpClient()) {
var response = await client.GetAsync(uri);
var htmlString = await response.Content.ReadAsStringAsync();
return htmlString;
}
}

WebClient uses HttpWebRequest, which unfortunately is not very asynchronous, even if you use the "asynchronous" methods. It does a blocking DNS lookup, at least. It may also block during proxy negotiation and/or the initial HTTP connection.
An older release of HttpClient was just using a wrapper around HttpWebRequest. I requested a truly-asynchronous HttpClient, but never heard a response. The last time I checked HttpClient, it was still part of MVC; ASP.NET Web API wasn't around at that time, so they may have fixed HttpClient since then. Or the difference in behavior between WebClient and HttpClient on your machine may just have to do with DNS caches or some such.

Related

Performance Issue: .NET HttpClient Response is slow

Problem:
I am using .NET Class HttpClient to make Requests to the endpoint URL.
My Code:
using (HttpClient apiClient1 = new HttpClient())
{
apiClient.GetAsync(apiUrl).Result;
}
Problem Identified:
If I use using block, I'm opening multiple connections, leads to socketExceptions.
Changed My Above Code to:
public class RequestController: ApiController
{
private static HttpClient apiClient = new HttpClient();
[HttpPost]
public dynamic GetWebApiData([FromBody] ParamData params)
{
var resultContent = apiClient.GetAsync(apiUrl).Result.Content;
return Newtonsoft.Json.JsonConvert.DeserializeObject<object>(resultContent.ReadAsStringAsync().Result);
}
}
Result of the above code after making HttpClient as static is as follows:
Only one Connection is established.
For Each request, I'm looking for the 200 milliseconds reduction in
Response Time.
What I Need:
I want to make conurrent calls atleast 50 calls to the end point with High-Speed response.
Kindly help me with this scenario.
Use the async API and stop calling .Result blocking calls.
public class RequestController: ApiController {
private static HttpClient apiClient = new HttpClient();
[HttpPost]
public async Task<IHttpActionResult> GetWebApiData([FromBody] ParamData data) {
var response = await apiClient.GetAsync(apiUrl);
var resultContent = response.Content;
var model = await resultContent.ReadAsAsync<dynamic>();
return Ok(model);
}
}
The default SSL connection idle timeout of HttpClient are 2 mins. So, after that you have to re handshake with the server. This maybe the root cause.
You could follow this article(https://www.stevejgordon.co.uk/httpclient-connection-pooling-in-dotnet-core) to extend the timeout. But the testing result from my side, I could only extend to 5 mins. After that, the connection will be closed.

Exception while posting to web API, HTTPClient already disposed

I'm posting a bytearray from an Android App in Xamarin.Forms to an .NET Core 2.0 WebAPI. However, I'm getting an exception saying that the NetworkStream already is disposed;
Code making the request;
public async Task PostImageAsync(ImageDTO image)
{
var content = new MultipartFormDataContent();
var byteArrayContent = new ByteArrayContent(image.Content);
content.Add(byteArrayContent, image.FileTile, image.FileName);
try
{
using (var httpClient = GetNewHttpClient())
{
SetBearerToken(httpClient);
var response = await httpClient.PostAsync($"{_apiUrl}/api/images/upload", content);
if (response.IsSuccessStatusCode)
{
}
else
{
}
}
}
catch (Exception e)
{
//Exception occurs here
var msg = e.GetBaseException().Message;
throw;
}
}
Code to get the HttpClient
private HttpClient GetNewHttpClient()
{
//HttpClientHandler is a global variable
var httpClient = new HttpClient(HttpClientHandler, false) {BaseAddress = new Uri(_apiUrl)};
return httpClient;
}
API Endpoint
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file == null || file.Length == 0) return BadRequest();
return Ok();
}
EDIT - SetBearerToken Method
private static void SetBearerToken(HttpClient client)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", App.StoredToken);
}
The Exception:
cannot access a disposed object. Object name: 'System.Net.Sockets.NetworkStream'.
It feels like a really obvious mistake I'm making here, but I can't get my head around it. Anybody has any ideas?
Don't dispose objects inside async functions
A using statement in an async method is "odd" in that the Dispose
call may execute in a different thread to the one which acquired the
resource (depending on synchronization context etc) but it will still
happen... assuming the thing you're waiting for ever shows up or
fail, of course. (Just like you won't end up calling Dispose in
non-async code if your using statement contains a call to a method
which never returns.)
#jon-skeet https://stackoverflow.com/a/16566605/2228916
Don’t dispose of the HttpClient:
https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
Also noticed that you set _apiUrl as the BaseAddress and prefix the url in the post. Pick one or the other.

Xamarin Android HttpClient PostAsync

I have an android app in Xamarin native. I am trying to consume a Restful service from an API in another server.
I have this:
private async Task<string> CreateCellphone(string url, Cellphone cell)
{
string cellphone = JsonConvert.SerializeObject(cell);
HttpContent content = new StringContent(cellphone, Encoding.UTF8, "application/json");
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.PostAsync(url, content);
string responseMessage = await response.Content.ReadAsStringAsync();
return responseMessage;
}
}
I execute this on a button call like this:
private void RegisterButtonOnClick(object sender, EventArgs e)
{
// Create new GUID
Guid obj = Guid.NewGuid();
// Store the created GUID in a private shared preferences file
var localGUID = Application.Context.GetSharedPreferences("LocalSetup", FileCreationMode.Private);
var guidEdit = localGUID.Edit();
guidEdit.PutString("GUID", obj.ToString());
guidEdit.PutBoolean("IsRegistered", true);
guidEdit.Commit();
// Create the cellphone record into the database for DB admin to activate
_url = Resources.GetString(Resource.String.cellphone_api_url);
Cellphone cell = new Cellphone();
cell.CellphoneId = obj.ToString();
var response = CreateCellphone(_url, cell);
}
But when my code gets to the postAsync method, nothing happens, it just continues without actually sending the code to the endpoint, I have no idea what I might be doing wrong, because all documentation I have on PostAsync tells me this is how to send json data for a Restful Web api endpoint.
Thank you in advance for any pointers.
You need to await the call to CreateCellphone or nothing will happen because the response Task will get disposed of almost immediately. Not sure if you can make your button click method async in Xamarin, but I would try this:
private async void RegisterButtonOnClick(object sender, EventArgs e)
//^^^^^
//Add this
{
//snip
await CreateCellphone(_url, cell);
}
Failing that, there are various way to call an async method synchronously, check this question.

C# HttpClient to post information without waiting for response

I am using HttpClient class in my asp.net web api 2 application to post some information to a endpoint. I just want to post the information without waiting for a response. Is this the right syntax
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP POST
var gizmo = new Product() { Name = "Gizmo", Price = 100, Category = "Widget" };
var response = await client.PostAsJsonAsync("api/products", gizmo);
}
I just want to post the information without waiting for a response
Not awaiting an async method in WebAPI will result in a runtime exception, as the AspNetSynchronizationContext is aware of any triggered asynchronous operations. If it notices a controller action completes before the async operation has, it will trigger the said exception. More on that in ASP.NET Controller: An asynchronous module or handler completed while an asynchronous operation was still pending
If you want to use a fire and forget semantics, you need to queue the delegate via HostingEnvironment.QueueBackgroundWorkItem if you're using .NET 4.5.2 and above. If not, you can defer to using BackgroundTaskManager
Keep in mind this kind of design isn't really suitable for WebAPI. It doesn't scale if you're triggering this action call frequently. If this style happens often, consider using something more suitable such as a message broker.
To implement the async Task in ASP.NET refer to the following sample syntax:
protected void Page_Load(object sender, EventArgs e)
{
try
{
RegisterAsyncTask(new PageAsyncTask(LoadUrlContent));
}
catch {}
}
protected async Task LoadUrlContent()
{
try
{
// Add your code here, for example read the content using HttpClient:
string _content = await ReadTextAsync(YourUrl, 10);
}
catch { throw; }
}
Also, set <%# Page ... Async="true" %> at page level.
Following sample code snippet shows the use of HttpClient (call this sample function from LoadUrlContent():
protected async Task<string> ReadTextAsync(string Url, int TimeOutSec)
{
try
{
using (HttpClient _client = new HttpClient() { Timeout = TimeSpan.FromSeconds(TimeOutSec) })
{
_client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/html"));
using (HttpResponseMessage _responseMsg = await _client.GetAsync(Url))
{
using (HttpContent content = _responseMsg.Content)
{
return await content.ReadAsStringAsync();
}
}
}
}
catch { throw; }
}
You can modify this code base pertinent to your particular task.
Hope this may help.

How to call asynchronous method synchronously in Windows Phone 8

We have existing iOS application developed using Xamarin.Forms. Now we want to extend to both Android and Windows Phone. In the existing application, all the web service calls are made synchronously. Windows phone supports only asynchronous calling, so we thought of wrapping the asynchronous calls to synchronous.
We are using HttpClient.PostAsync method to access the service. Once the execution hits PostAsync method, the phone hangs. The code to call the service is as follows:
private static async void CallService(System.Uri uri)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Host = uri.Host;
client.Timeout = System.TimeSpan.FromSeconds(30);
HttpContent content = new StringContent("", Encoding.UTF8, "application/xml");
var configuredClient = client.PostAsync(uri, content).ConfigureAwait(false);
var resp = configuredClient.GetAwaiter().GetResult();
resp.EnsureSuccessStatusCode();
responseString = resp.StatusCode.ToString();
resp.Dispose();
client.CancelPendingRequests();
client.Dispose();
}
}
I know this is because of blocking the UI thread, so only I implemented ConfigureAwait(false) but that didn't work at all. I tried with System.Net.WebClient also but the same result.
Now, how I will make this asynchronous call to process synchronously in Windows Phone 8?
First of all avoid using async void methods,because you can't easily wait for its completion. Return Task instead, being inside async method you don't need to do something special to return a Task. Compiler does all the work for you.
You need to await the call to HttpClient.PostAsync, that should be enough to keep the UI responsive.
private static async Task CallService(System.Uri uri)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Host = uri.Host;
client.Timeout = System.TimeSpan.FromSeconds(30);
HttpContent content = new StringContent("", Encoding.UTF8, "application/xml");
var resp = await client.PostAsync(uri, content);// Await it
resp.EnsureSuccessStatusCode();
responseString = resp.StatusCode.ToString();
resp.Dispose();
client.CancelPendingRequests();
}
}
Note: I've removed the ConfigureAwait(false) as that's not required. If you really need it, you may add it back.

Categories