monoh_ February 2016

WebClient progress reporting using TAP

I'm wondering if there is a way to report WebClient progress without using EAP(Event-based Asynchronous Pattern). Old way(using EAP) would be:

var client = new WebClient();
client.DownloadProgressChanged += (s,e) => { //progress reporting }
client.DownloadFileCompleted += (s,e) => { Console.Write("download finished" }
client.DownloadFileAsync(file);

With async/await this can be written as:

var client = new WebClient();
client.DownloadProgressChanged += (s,e) => { //progress reporting }
await client.DownloadFileTaskAsync(file);
Console.Write("downlaod finished");

But in the second example i'm using both EAP and TAP(Task-based Asynchronous Pattern). Isn't mixing two patterns of asynchrony considered as a bad practice?

Is there a way to achieve the same without using EAP? I have read about IProgress interface, but I think there is no way to use it to report WebClient progress.

Answers


Paulo Morgado February 2016

The bad news is that the answer is NO!

The good news is that any EAP API can be converted into a TAP API.

Try this:

public static class WebClientExtensios
{
    public static async Task DownloadFileTaskAsync(
        this WebClient webClient, 
        Uri address, 
        string fileName, 
        IProgress<Tuple<long, int, long>> progress)
    {
        // Create the task to be returned
        var tcs = new TaskCompletionSource<object>(address);

        // Setup the callback event handler handlers
        AsyncCompletedEventHandler completedHandler = (cs, ce) =>
        {
            if (ce.UserState == tcs)
            {
                if (ce.Error != null) tcs.TrySetException(ce.Error);
                else if (ce.Cancelled) tcs.TrySetCanceled();
                else tcs.TrySetResult(null);
            }
        };

        DownloadProgressChangedEventHandler progressChangedHandler = (ps, pe) =>
        {
            if (pe.UserState == tcs)
            {
                progress.Report(
                    Tuple.Create(
                        pe.BytesReceived, 
                        pe.ProgressPercentage, 
                        pe.TotalBytesToReceive));
            }
        };

        try
        {
            webClient.DownloadFileCompleted += completedHandler;
            webClient.DownloadProgressChanged += progressChangedHandler;

            webClient.DownloadFileAsync(address, fileName, tcs);

            await tcs.Task;
        }
        finally
        {
            webClient.DownloadFileCompleted -= completedHandler;
            webClient.DownloadProgressChanged -= progressChangedHandler;
        }
    }
}

And just use it like this:

void Main()
{
    var webClient = new WebClient();

    webClient.DownloadFileTaskAsync(
        new Uri("http://feeds.paulomorgado.net/paulomorgado/blogs/en"),
        @"c:\temp\feed.xml",
        new Pro 

Post Status

Asked in February 2016
Viewed 1,965 times
Voted 11
Answered 1 times

Search




Leave an answer