DownloadStringAsync() does not download the string asynchronously - c#

Trying to implement downloadStringAsync() to prevent UI freezing for 10 seconds when downloading one byte of data. However, even though the download completes, it is freezing the UI just as if I used downloadString().
Here is my code:
public void loadHTML()
{
WebClient client = new WebClient();
// Specify that the DownloadStringCallback2 method gets called
// when the download completes.
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(loadHTMLCallback);
client.DownloadStringAsync(new Uri("http://www.example.com"));
return;
}
public void loadHTMLCallback(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;
// Do cool stuff with result
}
}

Encountered the same problem, and found a solution.
Quite complex discussion here:
http://social.msdn.microsoft.com/Forums/en-US/a00dba00-5432-450b-9904-9d343c11888d/webclient-downloadstringasync-freeze-my-ui?forum=ncl
In short, the problem is web client is searching for proxy servers and hanging the app.
The following solution helps:
WebClient webClient = new WebClient();
webClient.Proxy = null;
... Do whatever else ...

Related

How to make windows service request wait until the previous request is complete

I am working on a window services application and my window service will call one of the web services in certain intervals (for example 3 min). From the web service I will get data from a database and using that data I will send an email.
If I am having huge sets of rows in my db table it will take some time to send the mail. Here I have the problem: The window services send the first request and it will handle some set of records. So, while processing it by the web service, the window service sends another request to the web service before it has completed the first request.
Due to this, the web service gets the same records from db again and again whenever it receives a new request from the windows service.
Can any one suggest me how to lock the previous request until it completes its work or some other way to handle this situation?
Web Service call:
protected override void OnStart(string[] args)
{
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 180000;
timer.AutoReset = false;
timer.Enabled = true;
}
Inside Method
using (MailWebService call = new MailWebService())
{
try
{
call.ServiceUrl = GetWebServiceUrl();
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
call.CheckMailQueue();
}
catch (Exception ex)
{
LogHelper.LogWriter(ex);
}
finally
{
}
}
The Monitor class works great for this scenario. Here is an example of how to use it:
// This is the object that we lock to control access
private static object _intervalSync = new object();
private void OnElapsedTime(object sender, ElapsedEventArgs e)
{
if (System.Threading.Monitor.TryEnter(_intervalSync))
{
try
{
// Your code here
}
finally
{
// Make sure Exit is always called
System.Threading.Monitor.Exit(_intervalSync);
}
}
else
{
//Previous interval is still in progress.
}
}
There is also an overload for TryEnter that allows you to specify timeout for entering the section.

Backgroundworker and UI interactivity

I am using a background worker for a time consuming operation ( To upload some data to server and download some data from server). All server communications are via http requests (GET,POST,PUT,Delete) and the code i used are
private void btnSync_Click(object sender, RoutedEventArgs e)
{
btnSync.IsEnabled = false;
BackgroundWorker syncWorker = new BackgroundWorker();
syncWorker.WorkerReportsProgress = true;
syncWorker.WorkerSupportsCancellation = true;
syncWorker.DoWork+=new DoWorkEventHandler(syncWorker_DoWork);
syncWorker.ProgressChanged+=new ProgressChangedEventHandler(syncWorker_ProgressChanged);
syncWorker.RunWorkerCompleted+=new RunWorkerCompletedEventHandler(syncWorker_RunWorkerCompleted);
syncWorker.RunWorkerAsync();
}
private void syncWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker target = (BackgroundWorker)sender;
foreach (...........) // a loop to do some set of actions on some objects
{
target.ReportProgress(1, obj);
target.ReportProgress(2, obj);
target.ReportProgress(3, obj);
}
target.ReportProgress(4);
}
private void syncWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 1)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id); //Issue a GET request and read and process resposne (deserialize json data and insert to local DB)
}
if (e.ProgressPercentage == 2)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id);//Issue a GET request and read and process resposne(deserialize json data and insert to local DB)
}
if (e.ProgressPercentage == 3)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id);//Issue a GET request and read and process resposne (deserialize json data and insert/Update response data to local DB)
}
if (e.ProgressPercentage ==4)
{
mySyncHelper.DOPOSTToServer();//Issue a POST request and read and process resposne (deserialize json data and insert/Update response data to local DB)
mySyncHelper.DOPUTToServer();
mySyncHelper.DODELETEToServer();
}
}
private void syncWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
txtSyncStatus.Visibility = Visibility.Visible;
txtSyncStatus.Text = string.Concat(DbookLanguage.message_error, Environment.NewLine , e.Error.Message.ToString());
}
else
{
btnSync.IsEnabled = true;
txtSyncStatus.Text = "";
}
}
The problem i am facing is # some stages the application is waiting some time for getting response back for the http requests and during this state the entire application goes to freezed state . The code i used for getting response from server is
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
and on this state the application is non responding .
How can i prevent this ? Is any other good way to make series of Http requests and make app UI interactive
As suggested in comments, the code in "ProgressChanged" runs on UI thread which is causing your UI to freeze (because you have server request in this handler).
ProgressChanged
handler is useful (and should be used) only when you have some regular UI updates (like percentage completed). In your cases, consider moving your entire code to a new method (say GetFromServer(int id)) and make call to this method from inside DoWork. This will cause all this I/O work running on background thread.

Using DownloadDataAsync with Cookie aware Webclient to get Update Progress

I am using a Webclient with a CookieContainer to download data from a webservice (Sharepoint).
I want to DownloadDataAsync so that I download multiple documents and update a single Progress Bar for each Document as it downloads. The non async version - DownloadData does not send Progress updates.
How do I get the Async version to wait at the doc.BinaryData = xx line before moving on the next Document?
How do I get the byte array from the DownloadFileCompleted event?
How can apply the changes to the progressbar without using DoEvents?
partial class Form()
{
void Main()
{
List urls= new List();
//urls.Add("xxxxxxx"); //get them from somewhere
for (int i = 0; i < urls.Count; i++)
{
var doc = new Document();
doc.BinaryData = DocumentAsArray(urls.ElementAt(i));
entities.AddToDocument(doc);
}
}
public byte[] DocumentAsArray(string URL)
{
byte[] #return = null;
using (CookieAwareWebClient client = new CookieAwareWebClient())
{
client.CookieContainer = spAuthentication.CookieContainer;
// Assign the events to capture the progress percentage
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadDataAsync(new Uri(URL));
}
return #return;
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBarControlFile.Position = e.ProgressPercentage;
var newPc = string.Format("Downloaded {0} %", e.ProgressPercentage);
labelControlFile.Text = newPc;
Application.DoEvents();
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
progressBarControlFile.Position = progressBarControlFile.Properties.Maximum;
labelControlFile.Text = string.Format("{0} %", progressBarControlFile.Properties.Maximum);
Application.DoEvents();
}
}
public class CookieAwareWebClient : WebClient
{
public CookieContainer CookieContainer { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
var webRequest = request as HttpWebRequest;
if (webRequest != null)
{
webRequest.CookieContainer = this.CookieContainer;
webRequest.KeepAlive = false;
}
return request;
}
}
}
You have to wait for the DownloadFileCompleted event. You can either set a volatile bool on which you poll or do this with an event. There are only minor differences performance-wise, but generally probably events are the cleaner solution (I prefer bool-polling). You might in your case actually want to wait for this to happen at the end of DocumentAsArray.
http://msdn.microsoft.com/en-us/library/ms144190.aspx The downloaded data is available in the Result property.
I don't know the DoEvents methods - if I understand correctly what you want to do, you want to update your UI? You would have to call InvokeEx (at least that is the easiest and.. only way I know). Look at this great :) article http://www.andreas-reiff.de/2011/06/accessing-a-winformwpf-control-from-another-thread-through-invoke/ . Alternatively, stay at stackoverflow and look at Best Way to Invoke Any Cross-Threaded Code?.
So to recap, if you just want the async to get the progress-update, simply change your DocumentAsArray function to wait for the DownloadFileCompleted event as outlined in 1. Then all you have to do is to be careful that you do not call your UI code from another thread - you should get an exception in Debug telling you not to do so (it runs fine in Release - most of the time). So use the InvokeEx-call.

WebClient does not support concurrent I/O operations

How can I get this error from with in the DownloadStringCompleted Event? Doesn't that mean, it's finished? Is there another event I can fire this from?
I get this error extremely rarely, but once in a while it will happen on my WP7 phone. I have a web client that I fire over and over, and I fire it again from the completed event. Is this happening because there is still some stale connection open? Is there a way to prevent this 100%?
I have checked to see if there is a chance for the thread to walk over itself, but it is only fired from within the completed event.
How can I be sure, when the complete event is fired, the client is no longer isBusy? One suggestion was to add a while with a thread sleep while the client is busy.
Some pseudo code.
var client = new WebClient("URL 1");
client.CompletedEvent += CompletedEvent;
client.downloadasync();
void CompletedEvent(){
Dosomestuff;
client.downloadasync(); //This is where we break.
}
The WebClient only supports a single operations, it cannot download multiple files. You haven't shown your code, but my guess is that you are somehow firing a new request before the old is completed. My bet is that WebClient.IsBusy is true when you attempt to perform another fetch.
See the following thread:
wb.DownloadFileAsync throw "WebClient does not support concurrent I/O operations." exception
The only answer is to create a new webclient within the scope of the Completed Event. You can't set it to new since webclient is readonly. Creating a new client is the only solution. This allows the old client to complete in the background. This does have slight memory implications since you are creating a new instance instead of reusing an old. But the garbage collector should keep it clean if your scope is setup right.
Instead of using WebClient use HttpClient to do parallel HTTP calls. Below code shows how to download files.
HttpClient httpClient = new HttpClient();
var documentList=_documentManager.GetAllDocuments();
documentList.AsParallel().ForAll(doc =>
{
var responseResult= httpClient.GetAsync(doc.FileURLPath);
using (var memStream = responseResult.Result.Content.ReadAsStreamAsync().Result)
{
using (var fileStream =File.Create($"{filePath}\\{doc.FileName}"))
{
memStream.CopyTo(fileStream);
}
}
});
The solution, I found is to use multiple WebClient objects, so to modify your pseudocode example; try
var client = new WebClient("URL 1");
client.CompletedEvent += CompletedEvent;
client.downloadasync();
void CompletedEvent(){
Dosomestuff;
var client2 = new WebClient();
client2.downloadasync();
}
Create a new Web Client for each new request. Don't reuse an existing Web Client instance.
This allows the first request to complete before starting the new one. This is a standard way of creating new requests.
private async Void SyncParcelStatus(List<string> Urls)
{
try
{
foreach (var URL in WebhookUrls)
{
Task.Factory.StartNew(() => AsyncDownLoad(URL));
}
}
catch (Exception ex)
{
//log Exception
}
}
private async void AsyncDownLoad(string URL)
{
using (WebClient myWebClient = new WebClient())
{
try
{
Uri StringToUri = new Uri(URL);
myWebClient.DownloadStringAsync(StringToUri);
}
catch (Exception ex)
{
//log Exception
}
}
}

What is the prefered or accepted method for testing proxy settings?

I have a lot of trouble with the internet connectivity in the program I am working on and it all seems to spawn from some issue with the proxy settings. Most of the issues at this point are fixed, but the issue I am having now is that my method of testing the proxy settings makes some users wait for long periods of time.
Here is what I do:
System.Net.WebClient webClnt = new System.Net.WebClient();
webClnt.Proxy = proxy;
webClnt.Credentials = proxy.Credentials;
byte[] tempBytes;
try
{
tempBytes = webClnt.DownloadData(url.Address);
}
catch
{
//Invalid proxy settings
//Code to handle the exception goes here
}
This is the only way that I've found to test if the proxy settings are correct. I tried making a web service call to our web service, but no proxy settings are needed when making the call. It will work even if I have bogus proxy settings. The above method, though, has no timeout member that I can set that I can find and I use the DownloadData as opposed to the DownloadDataAsync because I need to wait til the method is done so that I can know if the settings are correct before continuing on in the program.
Any suggestions on a better method or a work around for this method is appreciated.
Mike
EDIT: I tried something else, but no luck. I used the DownloadDataAsync method to download the data in a separate thread which raises the DownloadDataCompleted event of the WebClient when finished. While I wait for the event to get called I have a loop: while(DateTime.Now < downloadStart.AddMinutes(timeout) && !TestIsDone) {} The DownloadDataCompleted event sets the TestIsDone member to true when the event is called. The problem here is if the proxy settings are bad the Event never gets called, no exception is thrown, and the program waits for the entire timeout period before continuing. Here is the code for this approach:
public static bool TestProxy(System.Net.WebProxy proxy)
{
ProxySettingsTestDone = false; //public static var
string address = //url to some arbitrary data on our server
System.Net.WebClient webClnt = new System.Net.WebClient();
webClnt.Proxy = proxy;
webClnt.Credentials = proxy.Credentials;
try
{
webClnt.DownloadDataCompleted += new System.Net.DownloadDataCompletedEventHandler(DownloadDataCallback);
webClnt.DownloadDataAsync(new Uri(address));
//Timeout period
DateTime dnldStartTime = DateTime.Now;
while (DateTime.Now < dnldStartTime.AddMinutes(1.0) && !ProxySettingsTestDone)
{ }
if (!ProxySettingsTestDone) //Exceded timeout
{
throw new System.Net.WebException("Invalid Proxy Settings");
}
}
catch (System.Net.WebException e)
{
if (e.Status == System.Net.WebExceptionStatus.ProxyNameResolutionFailure)
{
//Proxy failed, server may or may not be there
Util.ConnectivityErrorMsg = e.Message;
return false;
}
else if (e.Status == System.Net.WebExceptionStatus.ProtocolError)
{
//File not found, server is down, but proxy settings succeded
ServerUp = false;
Util.ConnectivityErrorMsg = e.Message;
return true;
}
return false;
}
Util.ConnectivityErrorMsg = "";
return true;
}
private static void DownloadDataCallback(object sender, System.Net.DownloadDataCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
ProxySettingsTestDone = true;
else
throw new System.Net.WebException("Invalid Proxy Settings");
}
Sorry about the long post. I wanted to update this question with the information that I found after testing this new approach.
Thanks,
Mike
You can run the proxycheck in a seperate thread. And consider the check to be failed if the thread takes too long.
Or you could use WebRequest, it allows you set a timeout:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://url");
request.Proxy = proxy;
request.Timeout = 2000;
If the request has not finished within the given timeout a WebException with the Status property set to WebExceptionStatus.Timeout will be thrown.
Every method mentioned here are valid. But the most important one is to test the Proxy connection using the same Windows user account for the process that you want to test. Many proxies has specific privileges for each Windows user.
A recent post Testing IP:Port proxies explains proxy check using a simple Python script. The script checks for availability and correctness of proxy servers.

Categories

Resources