I am downloading some files from the internet using a WebClient
in the following way:
try
{
ManualResetEvent mr = new ManualResetEvent(false);
mr.Reset();
using (WebClient wc = new WebClient())
{
wc.DownloadFileCompleted += ((sender, args) =>
{
if (args.Error == null)
{
File.Move(filePath, Path.ChangeExtension(filePath, ".jpg"));
mr.Set();
}
else
{
//how to pass args.Error?
}
});
wc.DownloadFileAsync(new Uri(string.Format("{0}/{1}", Settings1.Default.WebPhotosLocation, Path.GetFileName(f.FullName))), filePath);
mr.WaitOne();
}
}
catch (Exception ex)
{
//Catch my error here and handle it (display message box)
}
But I cannot seem to pass the error from my annonymous DownloadFileCompleted
method up to my main catch. What is the correct way to do this?
What you can do is create a Task (an async operation) and use the ContinueWith directive to handle the exception. This can be a bit unreadable
However, with the introduction of .NET 4.5, Webclient exposes a task based async download for you!
Bad solution with rethrowing
You can save the exception in some variable defined outside the lambda. Then it can be rethrown:
Why is it bad? Because you will loose the stacktrace(it will show that the exception was thrown in the current method, not in the WebClient). Still, if you do not need or do not care about stacktrace, it is possible solution.
Handling the exception in place
You can also just create some method that will handle the exception in both the outer try-catch and in the downloaded handler:
Doing it right
The best idea is to avoid void methods on
WebClient
, because you can't await on them or apply some continuation.Such methods are convenient in some sense, but they force you to use clandestine solutions with synchronization constructs to make the workflow less dependent on different callbacks.
To use async-await you will have to apply
public Task<byte[]> DownloadDataTaskAsync(Uri address)
method.You can either:
1.
await
it to get the byte array of data to save it later manually, but it will require a solid rework in your application to make it async all the way:It will work, but I am not sure that
DownloadDataTaskAsync
is a true async method.2. So you may also consider using Task Continuations with the same method:
P.S.: And why don't you just use the simple blocking method
public void DownloadFile(Uri address, string fileName)
if you have no need to load files asynchronously?You should use
await
andDownloadFileTaskAsync
:DownloadFileAsync uses Event-based Asynchronous Pattern, you can't catch the exception, you can get exception throw AsyncCompletedEventArgs.Error Property