Reputation: 2456
This is just an imaginary problem, I'm hoping that the solution will help in whole range of similar scenarios. Suppose I need to count total size of all external resources on a webpage (images, scripts etc.). I download the page, extract all SRC information and transform the URL list into download tasks:
async Task<int> GetTotalSize(Uri uri) {
string[] urls = ... code to extract all external resources' URLs from given page ...
var tasks = from url in urls.Distinct()
select new WebClient().DownloadDataTaskAsync(new Uri(url));
var files = await TaskEx.WhenAll(tasks);
return files.Sum(file => file.Length);
}
Now, if one of the links is unreachable for any reason, the whole TaskEx.WhenAll is aborted with WebException. What I need is to ignore any WebExceptions inside individual tasks and assume length of 0 in that case. Any ideas?
Upvotes: 3
Views: 3135
Reputation: 5228
This solution allows for both asynchronous and parallel execution, which the currently accepted answer from Jeff does not.
var tasks = from url in urls.Distinct()
select new WebClient().DownloadDataTaskAsync(new Uri(url));
try
{
await TaskEx.WhenAll(tasks);
}
catch(Exception)
{
}
var files = tasks
.Where(f => !f.IsFaulted)
.Select(f => f.Result);
return files.Sum(file => file.Length);
Borrowed from https://stackoverflow.com/a/15857555/1152054
Upvotes: 3
Reputation: 6500
I don't think you can avoid the aggregateexception with the above example - aggregateexception makes sense as the exception could be caused by non-webexception as well, for example cancellation. I believe the right pattern should be to handle the exception gracefully with a try catch block and if "file" does not have exception sum it up. Reed has a nice post explaining the very same http://reedcopsey.com/2010/07/19/parallelism-in-net-part-18-task-continuations-with-multiple-tasks/
Hope it helps
Upvotes: 0
Reputation: 134521
Just add a separate (asynchronous) method to get the size of a single url. Then add them up.
e.g.,
static async Task<int> GetTotalSizeAsync(params string[] urls)
{
if (urls == null)
return 0;
var tasks = urls.Select(GetSizeAsync);
var sizes = await TaskEx.WhenAll(tasks);
return sizes.Sum();
}
static async Task<int> GetSizeAsync(string url)
{
try
{
var str = await new WebClient().DownloadStringTaskAsync(url);
return str.Length;
}
catch (WebException)
{
return 0;
}
}
Upvotes: 5