Now the WebClient issue is fixed and can return on a background thread i'd like to start using it in this way.
After much searching I've come up with this code that appears to work fine, is this all there is to it?
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s,e) =>
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += DownloadStringCompleted;
wc.DownloadStringAsync(url);
};
bw.RunWorkerAsync();
In DownloadStringCompleted I send the result back to the UI thread.
Have I missed anything important or is it really this simple?
I don't get why you would want to run the WebClient on a background thread in the first place, since the WebClient is already creating a thread for the downloading part.
The difference is that the WebClient is running it's DownloadStringCompleted event on the UI thread. Which it still would do in your code.
I would suggest you use WebRequest class instead. The use of the WebRequest class can be greatly simplified with a simple extension method, that makes it behave like the WebClient.
public static class WebRequestEx
{
public static void DownloadStringAsync(this WebRequest request, Action<string> callback)
{
if (request == null)
throw new ArgumentNullException("request");
if (callback == null)
throw new ArgumentNullException("callback");
request.BeginGetResponse((IAsyncResult result) =>
{
try
{
var response = request.EndGetResponse(result);
using (var reader = new StreamReader(response.GetResponseStream()))
{
callback(reader.ReadToEnd());
}
}
catch (WebException e)
{
// Don't perform a callback, as this error is mostly due to
// there being no internet connection available.
System.Diagnostics.Debug.WriteLine(e.Message);
}
}, request);
}
}
The issue I referred to was that in 7.0 WebClient always returned on the UI thread regardless of where it was created, potentially making the UI unresponsive.
In the WP SDK 7.1 WebClient will return on the thread it was created from, so if it is created from a background thread DownloadStringCompleted will now return on a background thread.
If you test my example without marshalling the response you will see an Invalid Cross Thread Exception.
It seems to me unless you have a reason not to, why not use WebClient now?
Seems that easy.
Check only if everything which can be disposed get's disposed.
Related
I'm trying to understand the differences among WebClient.OpenRead, WebClient.OpenReadAsync and WebClient.OpenReadTaskAsync.
It looks like these have differences regarding blocking a thread, but I don't understand it well.
Could you please explain the differences? It would be great if you could give me some example (examples don't have to be sample code, but would be great if you could provide)
As you said, the difference is in thread blocking behavior. First one (OpenRead()) is thread blocking operation, other two - not. For example, let assume, that your network latency to reach google.com is 300ms. When you do var stream = webClient.OpenRead(#"https://www.google.com"); your application is "paused" for this 300ms, so code next to this line is not executed until your webClient return a stream to stream variable. This is calling Thread blocking.
When you do this in UI-thread (in example: in a button click handler) - your application become freezing and not responding to user actions. This is bad user experience, so never-ever call a thread blocking stuff in your UI. Here is example for console application:
var address = #"https://www.google.com/";
Console.WriteLine($"Opening stream from {address}");
using (var stream = webClient.OpenRead(address)) // this will block for 300ms
{
Console.WriteLine("Stream is open!");
// some other code
}
Second method (OpenReadAsync()) is asynchronous and return nothing immediately after a call, so your thread is not blocked. After awhile (300ms) OpenReadCompleted event will be raised by your webClient and all attached listeners will handle opened stream one-by-one. Here is an example:
public partial class MainForm : Form
{
private WebClient _client = new WebClient();
public MainForm()
{
InitializeComponents();
_client.OpenReadCompleted += OpenReadCompletedHandler;
}
private void ButtonClickHandler(object sender, EventArgs e)
{
_client.OpenReadAsync(#"https://www.google.com/");
}
private void OpenReadCompletedHandler(object sender, OpenReadCompletedEventArgs e)
{
// this event will be raiesed 300ms after 'Button' click
var stream = e.Result; // <- here is your stream
// some other code
}
}
The last one (OpenReadTaskAsync()) is all about TPL (Task Parallel Library) and async/await keywords. It runs all stuff in a Task which is returned by this method. Here is an example:
public partial class MainForm : Form
{
private WebClient _client = new WebClient();
public MainForm()
{
InitializeComponents();
}
private async void ButtonClickHandler(object sender, EventArgs e)
{
// after 'await' keyword, execution will be returned from this method immediately
// meanwhile, actual acquiring of 'stream' is running in a background thread
using (var stream = await _client.OpenReadTaskAsync(#"https://www.google.com/"))
{
// after 300ms, this code will be continued in UI thread
// result will be automaticly unpacked to 'stream' variable
// some other code
}
}
}
Hope this helps.
I'd suggest WebClient is more or less obsolete now, HttpClient is more appropriate for anything targeting .NET framework 4.5+ or .NET core. Just watch out that the latter does not automatically throw exceptions on HTTP error codes (400+).
Here is what i have done so far:
All of the below code will get looped through and different URL's will be sent each time. I want to be able to call the address in the loop and then wait for it to complete and then call the completed method.
I have created my URI
Uri address = new Uri("http://dev.virtualearth.net/REST/V1/Imagery/Metadata/OrdnanceSurvey/" + latitude + "," + longitude + "?+zl=" + zoomLevel + "&key=""");
I have then told it where to call when the operation has complete
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += WebClientDownloadString_Complete;
webClient.DownloadStringAsync(address);
Then i set up what to happen when the operation has completed
private void WebClientDownloadString_Complete(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
string html = e.Result;
string[] parts = html.Split(',');
string[] URLs = parts[7].Split('"');
URL = URLs[3].Replace("{subdomain}", "t0").Replace("{quadkey}", qk.Key).Replace(#"\", string.Empty);
}
}
Is there a way so that when the webclient calls the URL I wait till the operation has completed and it calls the completed method?
The easy way to do this is to just use DownloadString instead of DownloadStringAsync. The DownloadString call will block (make sure to run it on a background thread!) and you can loop as you expect.
Your other option is to create a TaskCompletionSource and set it in your event handler.
private TaskCompletionSource<bool> currentTask;
private async Task GetURLs()
{
while (someCondition)
{
currentTask = new TaskCompletionSource<bool>();
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += WebClientDownloadString_Complete;
webClient.DownloadStringAsync(address);
await currentTask;
}
}
private void WebClientDownloadString_Complete(...)
{
//Process completion
//C# 6 null check
curentTask?.TrySetValue(true);
}
"GetURLS" will block asynchronously until the tasks value is set, and then allow the while loop to continue. This has the advantage of not needing an explicit background thread.
The code that you put up already does just that - you created your WebClient, you set an event handler to its completed method, and you start the download. If you want to wait for it to finish, all you need to do is let your method end, and control will pass back to the Silverlight framework which will do your waiting for you.
The other way to do this would be to use the async and await keywords, which your framework appears to support. Simply put, you might be able to put an await keyword before your method call to DownloadStringAsync, which will cause the system to wait for the download (and go on to other tasks), pull out the string once the download completes, and continue as usual:
string html = await webClient.DownloadStringAsync(address);
Wanted to do this myself some time ago.
Here is how I did it:
// create a notifier which tells us when the download is finished
// in .net 4.5 DownloadFileTaskAsync can be used instead which simplyfies this task
AutoResetEvent notifier = new AutoResetEvent(false);
client.DownloadFileCompleted += (object sender, AsyncCompletedEventArgs e) =>
{
notifier.Set();
};
try
{
client.DownloadFileAsync(downloadUrl, destination);
// wait for download to finish
notifier.WaitOne();
...
I'm busy with a windows phone application that of course uses silverlight. This means that calling any webservices has to be done asynchronously, and since this is all good and well in regards to best practice in preventing your entire app in hanging when waiting for a resource, I'm still stuck in the "synchronous mindset"...
Because the way I see it now is that you end up having 2 methods that needs to handle one function, e.g:
1)The method that actually calls the webservice:
public void myAsyncWebService(DownloadStringCompletedEventHandler callback)
{
//Url to webservice
string servletUrl = "https://deangrobler.com/someService/etc/etc"
//Calls Servlet
WebClient client = new WebClient();
client.DownloadStringCompleted += callback;
client.DownloadStringAsync(new Uri(servletUrl, UriKind.Absolute));
}
2) and the method that handles the data when it eventually comes back:
private void serviceReturn(object sender, DownloadStringCompletedEventArgs e)
{
var jsonResponse = e.Result;
//and so on and so forth...
}
So instead of having to just create and call a single method that goes to the webservice, gets the returned result and sent it back to me like this:
public string mySyncWebService(){
//Calls the webservice
// ...waits for return
//And returns result
}
I have to in a Class call myAsyncWebService, AND create another method in the calling class that will handle the result returned by myAsyncWebService. Just, in my opinion, creates messy code. With synchronous calls you could just call one method and be done with it.
Am I just using Asynchronous calls wrong? Is my understanding wrong? I need some enlightment here, I hate doing this messy-async calls. It makes my code too complex and readability just goes to... hell.
Thanks for anyone willing to shift my mind!
You have to turn your mind inside out to program asynchronously. I speak from experience. :)
Am I just using Asynchronous calls wrong? Is my understanding wrong?
No. Asynchronous code is fairly difficult to write (don't forget error handling) and extremely difficult to maintain.
This is the reason that async and await were invented.
If you're able to upgrade to VS2012, then you can use Microsoft.Bcl.Async (currently in beta) to write your code like this:
string url1 = "https://deangrobler.com/someService/etc/etc";
string jsonResponse1 = await new WebClient().DownloadStringTaskAsync(url1);
string url2 = GetUriFromJson(jsonResponse1);
string jsonResponse2 = await new WebClient().DownloadStringTaskAsync(url2);
Easy to write. Easy to maintain.
Async is like when you make a telephone call and get an answering machine, if you want a return call you leave your number. The first method is your call asking for data, the second is the "number" you've left for the return call.
It all becomes much easier and readable if you use lambdas instead. This also enables you to access variables declared in the "parent" method, like in the following example:
private void CallWebService()
{
//Defined outside the callback
var someFlag = true;
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
//Using lambdas, we can access variables defined outside the callback
if (someFlag)
{
//Do stuff with the result.
}
};
client.DownloadStringAsync(new Uri("http://www.microsoft.com/"));
}
EDIT: Here is another example with two chained service calls. It still isn't very pretty, but imho it is a little more readable than the OPs original code.
private void CallTwoWebServices()
{
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
//1st call completed. Now make 2nd call.
var client2 = new WebClient();
client2.DownloadStringCompleted += (s2, e2) =>
{
//Both calls completed.
};
client2.DownloadStringAsync(new Uri("http://www.google.com/"));
};
client.DownloadStringAsync(new Uri("http://www.microsoft.com/"));
}
To avoid creating messy code, if you can't use the async / await pattern because you are on older framework, you will find helpful check CoRoutines in their Caliburn Micro implemantation. With this pattern you create an enumerable yielding at each turn a new asynchronous segment to execute: by the reader point of view asynchronous steps appear as a sequence, but walking among the steps ( so yielding the next one ) is done externally by asynchronously wait the single task. It is a nice pattern easy to implement and really clear to read.
BTW if you don't want to use Caliburn Micro as your MVVM tool because you are using something else, you can use just the coroutine facility, it is very insulated inside the framework.
Let me just post some code from an example in this blog post.
public IEnumerable<IResult> Login(string username, string password)
{
_credential.Username = username;
_credential.Password = password;
var result = new Result();
var request = new GetUserSettings(username);
yield return new ProcessQuery(request, result, "Logging In...");
if (result.HasErrors)
{
yield return new ShowMessageBox("The username or password provided is incorrect.", "Access Denied");
yield break;
}
var response = result.GetResponse(request);
if(response.Permissions == null || response.Permissions.Count < 1)
{
yield return new ShowMessageBox("You do not have permission to access the dashboard.", "Access Denied");
yield break;
}
_context.Permissions = response.Permissions;
yield return new OpenWith<IShell, IDashboard>();
}
Isn't it easy to read? But it is is actually asynchronous: each yield steps are executed in an asynchronous manner and the execution flow again after the yield statement as soon the previous task completed.
With synchronous calls you could just call one method and be done with it.
Sure, but if you do that from the UI thread you will block the entire UI. That is unacceptable in any modern application, in particular in Silverlight applications running in the browser or in the phone. A phone that is unresponsive for 30 seconds while a DNS lookup times out is not something anybody wants to use.
So on the UI thread, probably because the user did some action in the UI, you start an asynchronous call. When the call completes a method is called on a background thread to handle the result of the call. This method will most likely update the UI with the result of the asynchronous call.
With the introduction of async and await in .NET 4.5 some of this "split" code can be simplified. Luckily async and await is now available for Windows Phone 7.5 in a beta version using the NuGet package Microsoft.Bcl.Async.
Here is a small (and somewhat silly) example demonstrating how you can chain two web service calls using async. This works with .NET 4.5 but using the NuGet package linked above you should be able to do something similar on Windows Phone 7.5.
async Task<String> GetCurrencyCode() {
using (var webClient = new WebClient()) {
var xml = await webClient.DownloadStringTaskAsync("http://freegeoip.net/xml/");
var xElement = XElement.Parse(xml);
var countryName = (String) xElement.Element("CountryName");
return await GetCurrencyCodeForCountry(countryName);
}
}
async Task<String> GetCurrencyCodeForCountry(String countryName) {
using (var webClient = new WebClient()) {
var outerXml = await webClient.DownloadStringTaskAsync("http://www.webservicex.net/country.asmx/GetCurrencyByCountry?CountryName=" + countryName);
var outerXElement = XElement.Parse(outerXml);
var innerXml = (String) outerXElement;
var innerXElement = XElement.Parse(innerXml);
var currencyCode = (String) innerXElement.Element("Table").Element("CurrencyCode");
return currencyCode;
}
}
However, you still need to bridge between the UI thread and the async GetCurrencyCode. You can't await in an event handler but you can use Task.ContinueWith on the task returned by the async call:
void OnUserAction() {
GetCurrencyCode().ContinueWith(GetCurrencyCodeCallback);
}
void GetCurrencyCodeCallback(Task<String> task) {
if (!task.IsFaulted)
Console.WriteLine(task.Result);
else
Console.WriteLine(task.Exception);
}
Can someone point me to a tutorial or provide some sample code to call the System.Net.WebClient().DownloadString(url) method without freezing the UI while waiting for the result?
I assume this would need to be done with a thread? Is there a simple implementation I can use without too much overhead code?
Thanks!
Implemented DownloadStringAsync, but UI is still freezing. Any ideas?
public void remoteFetch()
{
WebClient client = new WebClient();
// Specify that the DownloadStringCallback2 method gets called
// when the download completes.
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(remoteFetchCallback);
client.DownloadStringAsync(new Uri("http://www.google.com"));
}
public void remoteFetchCallback(Object sender, DownloadStringCompletedEventArgs e)
{
// If the request was not canceled and did not throw
// an exception, display the resource.
if (!e.Cancelled && e.Error == null)
{
string result = (string)e.Result;
MessageBox.Show(result);
}
}
Check out the WebClient.DownloadStringAsync() method, this'll let you make the request asynchronously without blocking the UI thread.
var wc = new WebClient();
wc.DownloadStringCompleted += (s, e) => Console.WriteLine(e.Result);
wc.DownloadStringAsync(new Uri("http://example.com/"));
(Also, don't forget to Dispose() the WebClient object when you're finished)
You could use a BackgroundWorker or as #Fulstow said the DownStringAsynch method.
Here's a tutorial on Backgorund worker: http://www.dotnetperls.com/backgroundworker
I use WebBrowser Control in c# to check a couple search engine results but sometime for no reason it gets stuck. My first thought was to make each searching function as a thread and use thread.abort() from a timer but I just couldn't handle with the UI Controls (including the WebBrowser) no matter what I've tried.
Anyone has a solution for me? an example would be great cause I already tried so many things and I keep getting all these exceptions.
I believe you can use WebRequest in a Background worker & avoid the difficulties of dealing with the COM threading model. You can use WebRequest.Timeout to handle anything you think is taking too long.
Try something like this:
static void Main(string[] args)
{
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += backgroundWorker_DoWork;
bg.RunWorkerAsync(new List<object> { "http://download.thinkbroadband.com/512MB.zip" });
while (true) {}
}
private static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Starting operation.");
BackgroundWorker bw = sender as BackgroundWorker;
List<object> args = (List<object>)e.Argument;
var url = (string)args[0];
WebRequest request = WebRequest.Create(url);
request.Timeout = 300;
try
{
WebResponse response = request.GetResponse();
Console.WriteLine("Request successful.");
}
catch (Exception ex)
{
Console.WriteLine("Request timed out.");
}
}
WebBrowser is a component you should use when you want to embed an instance of IE into your presentation layer. Unless you need this, you can use something more lightwweight.
Use the control's .Invoke() method: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
There are other methods you can use too: .BeginInvoke and .EndInvoke for asynchronous invokes.
if(control.InvokeRequired)
{
control.Invoke(control.delegate);
}