I can't seem to get async to work the way I want. I'm pretty unfamiliar with it in general, but my research has led me to it. Here's my situation: I have a XAML form I'm loading that needs data from a web service. I can get the data, no problem. The issue is that I want the form to load with a "Please wait. Data loading..." screen that I have made, but nothing loads until the query finishes which can take up to 10 seconds, which is a poor user experience. Can anyone let me know what I'm doing wrong?
In my ViewModel main section, I call LoadData() to get the ball rolling.
I also had a thought...is async what I really want here?
public async void LoadData()
{
IsLoading = Visibility.Visible;
MonitorData downloadInfo = await GetWebApiInfo();
try
{
AssignDataToControls(downloadInfo);
IsLoading = Visibility.Collapsed;
Console.WriteLine("Loaded successfully.");
}
catch (Exception e)
{
IsLoading = Visibility.Collapsed;
Console.WriteLine("Error: " + e);
}
}
private void AssignDataToControls(MonitorData mon)
{
MainPanel.Clear();
mon.MainPanel.ToList().ForEach(x => MainPanel.Add(x));
Information = mon.Information;
MonitorText = mon.MonitorText;
ProgressData = mon.progList;
}
public async Task<MonitorData> GetWebApiInfo()
{
main = new MonitorData();
var url = "::::::::WEB API ADDRESS::::::::";
var request = (HttpWebRequest)WebRequest.Create(url);
//Task<HttpWebRequest> req = await (HttpWebRequest)WebRequest.Create(url);
//HttpWebRequest request = await GetWebRequest(url);
WebResponse response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream, Encoding.Unicode);
string responseFromServer = reader.ReadToEnd();
var deserializer = new JavaScriptSerializer();
main = deserializer.Deserialize<MonitorData>(responseFromServer);
reader.Dispose();
response.Dispose();
return main;
}
The compiler will tell you exactly what's wrong with that approach. Specifically, your GetWebApiInfo method is going to run synchronously because you never use await.
In this case, you can use HttpClient to download the information asynchronously. It's a lot easier than mucking around with WebResponse and stuff:
private static readonly HttpClient _client = new HttpClient();
public async Task<MonitorData> GetWebApiInfo()
{
var url = "::::::::WEB API ADDRESS::::::::";
string responseFromServer;
using (var dataStream = await _client.GetStreamAsync())
using (var reader = new StreamReader(dataStream, Encoding.Unicode))
responseFromServer = await reader.ReadToEndAsync();
var deserializer = new JavaScriptSerializer();
return deserializer.Deserialize<MonitorData>(responseFromServer);
}
In general, when you want to "make something async", you should start from the lowest-level APIs. E.g., don't start by marking GetWebApiInfo with async; start with the actual HTTP transfer and call it with await. Then let async/await grow from there.
You may find my NotifyTask<T> type helpful; it allows you to do things like show/hide busy indicators with data binding.
Related
Hi I need to write a proper async web request function in C# to get some data in JSON format and deserialize it in strongly typed object. I came up with this solution:
public async Task<TReturnType> MakeAsyncRequest<TReturnType>(string url) where TReturnType : class
{
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
var response = await request.GetResponseAsync();
var json = await Task.Run(() => ReadStreamFromResponse(response));
var deserializedObject = await Task.Run(() => JsonConvert.DeserializeObject<TReturnType>(json));
return deserializedObject;
}
catch (Exception)
{
// TODO: Handle exception here
throw;
}
}
private string ReadStreamFromResponse(WebResponse response)
{
using (var responseStream = response.GetResponseStream())
using (var sr = new StreamReader(responseStream))
{
return sr.ReadToEnd();
}
}
And usage would be like this:
var myData = await MakeAsyncRequest<IEnumerable<SomeObject>>(http://example.com/api/?getdata=all);
The issue I'm facing is that sometimes my web request fires and never returns, is there a proper way to handle that? Maybe playing with timers is an option but I don't like to go there.
Also I would be thankful if you could improve on my function, maybe there is a better way to do things and I just don't see it.
I am trying to make a GET request to my backend via a Windows Mobile app that I am working on. The request stops in between and doesn't get through. It ends with no error.
Here's my code:
public void LoginClick() {
var myTask = LoginUser(email, password);
string result = myTask.Result;
}
private async Task<string> LoginUser(string email, string password)
{
//Declarations of Variables
string result = "";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://MYmobileservice.azure-mobile.net/api/loginuser?email="+email+"&password="+password);
request.ContinueTimeout = 4000;
request.Credentials = CredentialCache.DefaultNetworkCredentials;
//Add headers to request
request.Headers["Content-Type"] = "application/json";
request.Headers["ACCEPT"] = "application/json";
request.Headers["X-ZUMO-APPLICATION"] = "<SERVER-KEY-THE-APP-NEEDS>";
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
//To obtain response body
using (Stream streamResponse = response.GetResponseStream())
{
using (StreamReader streamRead = new StreamReader(streamResponse, Encoding.UTF8))
{
result = streamRead.ReadToEnd();
}
}
}
}
return result;
}
Why does it end without completing? Is there anything wrong with my code?
You need to use async all the way up. Here's how to re-write the event handler:
public async void LoginClick() {
string result = await LoginUser(email, password);
}
I'm making the assumption that LoginClick is referenced by a delegate to a Login button's Click event. Notice that I decorated the method with an async modifier. Also, notice that the code is now awaiting the Task returned by LoginUser. You might want to use the convention of LoginUserAsync, but that isn't required.
Wrap your code in the LoginUser method in a try/catch block. Set a breakpoint in the catch block to examine any exceptions thrown. You can look at the documentation of the HttpWebRequest and related types to see what exceptions they could be throwing.
Chances are that just using the proper async code will solve your problem because calling myTask.Result or myTask.Wait() will block the UI thread, which is your most likely problem.
I have this code:
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(URL);
using (WebResponse myResponse = myRequest.GetResponse())
{
using (StreamReader sr = new StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8))
{
result = sr.ReadToEnd();
}
}
I want on the finish this request, run this code:
MessageBox.Show("Finish");
Also, when i run this code, my program become freeze. I think this problem solve with async HttpWebRequest.
You can do it like (in an async method):
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(URL);
using (WebResponse myResponse = await myRequest.GetResponseAsync())
{
using (StreamReader sr = new StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8))
{
result = sr.ReadToEnd();
}
}
MessageBox.Show("Finish");
But I would recommend to use HttpClient
using (var client = new HttpClient())
{
var result = await client.GetStringAsync(URL);
}
MessageBox.Show("Finish");
BTW: By using GetStringAsync you use the encoding your site is using which may not be UTF8
EDIT
In order to "There are many different ways" that stated in original answer, its better to consider using async/await pattern that is done by Eser in his answer which is better answer
When there is async method like GetResponseAsync to do such job, its better to use it and avoid creating task.
Consider reading great posts of Stephen Cleary:
Async/Await - Best Practices in Asynchronous Programming
UI Guidelines for Async
Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation
ORIGINAL
There are many different ways, for example:
string url= "http://www.google.com";
string result = "";
Task.Run(() =>
{
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(url);
using (WebResponse myResponse = myRequest.GetResponse())
{
using (StreamReader sr = new StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8))
{
result = sr.ReadToEnd();
}
}
}).ContinueWith(x =>
{
//MessageBox.Show(result);
MessageBox.Show("Finished");
//here you can not easily set properties of form
//you should use Invoke pattern
});
This way the application will be completely responsive while the task is executing and there is no freezing anymore.
You can use HttpClient in System.Net.Http for this(Assuming .net > 4.5):
HttpClient client = new HttpClient();
var code = client.GetAsync(URL).ContinueWith(x =>
{
MessageBox.Show("finish");
client.Dispose();
});
You should add some error checking in continue with to be sure.
In my app I need to do lot of parallel http requests and I have read that it is proper to do it using async/await. In each request I need to get string content from it (often it is html of some site) and my question is: how can I do it in best way?
My current implementation:
public static async Task<string> GetStringContentAsync(HttpWebRequest webRequest)
{
try
{
using (var response = (HttpWebResponse) await webRequest.GetResponseAsync()
.ConfigureAwait(false))
{
var content = await GetStringContentFromResponseAsync(response)
.ConfigureAwait(false);
return content;
}
}
catch (Exception exception)
{
return null;
}
}
private static async Task<string> GetStringContentFromResponseAsync(HttpWebResponse response)
{
using (var responseStream = GetResponseStream(response))
{
if (responseStream == null)
return null;
using (var streamReader = new StreamReader(responseStream))
{
var content = await streamReader.ReadToEndAsync()
.ConfigureAwait(false);
return content;
}
}
}
private static Stream GetResponseStream(HttpWebResponse webResponse)
{
var responseStream = webResponse.GetResponseStream();
if (responseStream == null)
return null;
Stream stream;
switch (webResponse.ContentEncoding.ToUpperInvariant())
{
case "GZIP":
stream = new GZipStream(responseStream, CompressionMode.Decompress);
break;
case "DEFLATE":
stream = new DeflateStream(responseStream, CompressionMode.Decompress);
break;
default:
stream = responseStream;
break;
}
return stream;
}
And example of using:
var httpWebRequest = (HttpWebRequest) WebRequest.Create("http://stackoverflow.com/");
var content = await HttpHelper.GetStringContentAsync(httpWebRequest)
.ConfigureAwait(false);
Is this correct implementation, or we can improve something here? Maybe I'm doing some overhead when using async/await when reading stream?
Reason of my question is that when I'm using my code like this:
for(var i=0;i<1000;i++)
{
Task.Run(()=>{
var httpWebRequest = (HttpWebRequest) WebRequest.Create("http://google.com/");
var content = await HttpHelper.GetStringContentAsync(httpWebRequest)
.ConfigureAwait(false);
});
}
this tasks take to long to execute, but one request to google is very fast. I thought that async requests in this example must be ready almost in same time and this time must be pretty close to "one google request" time.
EDIT:
I forgot to say that I know about ServicePointManager.DefaultConnectionLimit and set it 5000 in my app. So it is not a problem.
I can't use HttpClient because my final goal is to do 100-300 requests at one time from different proxies. And if I understand right, HttpClient can work with only one proxy at one time and can't setup each request separately.
That's a tricky one. Since you know about DefaultConnectionLimit, it's already something good, but there is one more interesting and rather surprising thing:
httpRequest.ServicePoint.ConnectionLeaseTimeout
httpRequest.ServicePoint.MaxIdleTime
Information is here, your latencies might be caused by its default behavior and connections being held to ServicePoint while trying to make next request
Here's the answer answer to your issue: https://msdn.microsoft.com/en-us/library/86wf6409(v=vs.90).aspx
Using synchronous calls in asynchronous callback methods can result in severe performance penalties. Internet requests made with WebRequest and its descendants must use Stream.BeginRead to read the stream returned by the WebResponse.GetResponseStream method.
That means absolutely no synchronous code (including awaits) when reading the response stream. But even that isn't enough, as DNS lookups and TCP connection are still blocking. If you can use .NET 4.0, there's a much more easy to use System.Net.Http.HttpClient class. Otherwise, you can use System.Threading.ThreadPool, which is the workaround I ended up using on 3.5:
ThreadPool.QueueUserWorkItem((o) => {
// make a synchronous request via HttpWebRequest
});
Im wondering if theres an easy way to get the response of an async httpwebrequest.
I have already seen this question here but all im trying to do is return the response (which is usually json or xml) in the form of a string to another method where i can then parse it/ deal with it accordingly.
Heres some code:
I have these two static methods here which i think are thread safe as all the params are passed in and there are no shared local variables that the methods use?
public static void MakeAsyncRequest(string url, string contentType)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = contentType;
request.Method = WebRequestMethods.Http.Get;
request.Timeout = 20000;
request.Proxy = null;
request.BeginGetResponse(new AsyncCallback(ReadCallback), request);
}
private static void ReadCallback(IAsyncResult asyncResult)
{
HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
try
{
using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult))
{
Stream responseStream = response.GetResponseStream();
using (StreamReader sr = new StreamReader(responseStream))
{
//Need to return this response
string strContent = sr.ReadToEnd();
}
}
manualResetEvent.Set();
}
catch (Exception ex)
{
throw ex;
}
}
Assuming the problem is that you're having a hard time getting to the returned content, the easiest path would likely be using async/await if you can use it. Even better would be to switch to HttpClient if you're using .NET 4.5 since it's 'natively' async.
Using .NET 4 and C# 4, you can still use Task to wrap these and make it a bit easier to access the eventual result. For instance, one option would be the below. Note that it has the Main method blocking until the content string is available, but in a 'real' scenario you'd likely pass the task to something else or string another ContinueWith off of it or whatever.
void Main()
{
var task = MakeAsyncRequest("http://www.google.com", "text/html");
Console.WriteLine ("Got response of {0}", task.Result);
}
// Define other methods and classes here
public static Task<string> MakeAsyncRequest(string url, string contentType)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = contentType;
request.Method = WebRequestMethods.Http.Get;
request.Timeout = 20000;
request.Proxy = null;
Task<WebResponse> task = Task.Factory.FromAsync(
request.BeginGetResponse,
asyncResult => request.EndGetResponse(asyncResult),
(object)null);
return task.ContinueWith(t => ReadStreamFromResponse(t.Result));
}
private static string ReadStreamFromResponse(WebResponse response)
{
using (Stream responseStream = response.GetResponseStream())
using (StreamReader sr = new StreamReader(responseStream))
{
//Need to return this response
string strContent = sr.ReadToEnd();
return strContent;
}
}
"Even better would be to switch to HttpClient if you're using .Net 4.5 since it's 'natively' async." - absolutely right answer by James Manning.
This question was asked about 2 years ago. Now we have .Net Framework 4.5, which provides powerful asynchronous methods. Use HttpClient. Consider the following code:
async Task<string> HttpGetAsync(string URI)
{
try
{
HttpClient hc = new HttpClient();
Task<Stream> result = hc.GetStreamAsync(URI);
Stream vs = await result;
StreamReader am = new StreamReader(vs);
return await am.ReadToEndAsync();
}
catch (WebException ex)
{
switch (ex.Status)
{
case WebExceptionStatus.NameResolutionFailure:
MessageBox.Show("domain_not_found", "ERROR",
MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
//Catch other exceptions here
}
}
}
To use HttpGetAsync(), make a new method that is "async" too. async is required, because we need to use "await" in GetWebPage() method:
async void GetWebPage(string URI)
{
string html = await HttpGetAsync(URI);
//Do other operations with html code
}
Now if you want to get web-page HTML source code asynchronously, just call GetWebPage("web-address..."). Even Stream reading is asynchronous.
NOTE: to use HttpClient .Net Framework 4.5 is required. Also you need to add System.Net.Http reference in your project and add also "using System.Net.Http" for easy access.
For further reading how this approach works, visit: http://msdn.microsoft.com/en-us/library/hh191443(v=vs.110).aspx
Use of Async: Async in 4.5: Worth the Await
Once you go async, you can never go back. From there you only really have access to the async's callback. you can ramp up the complexity of this and do some threading & waithandles but that can be rather a painful endeavor.
Technically, you can also sleep the thread when you need to wait for the results, but I don't recommend that, you may as well do a normal http request at that point.
In C# 5 theyhave async/await commands that will make it easier to get the results of the async call to the main thread.
public static async Task<byte[]> GetBytesAsync(string url) {
var request = (HttpWebRequest)WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var content = new MemoryStream())
using (var responseStream = response.GetResponseStream()) {
await responseStream.CopyToAsync(content);
return content.ToArray();
}
}
public static async Task<string> GetStringAsync(string url) {
var bytes = await GetBytesAsync(url);
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}