Ladislav
Ladislav

Reputation: 329

C# exception on cancellation of background task

I have a simple HttpClient downloading solution where I need to support cancel download. Download itself works perfectly, however when I attempt to cancel download, I receive following exception:

Cannot access a disposed object. Object name: 'SslStream'

My code is the following:

var cts = new CancellationTokenSource();
var client = new HttpClient() { Timeout = TimeSpan.FromMinutes(5) };

DownloadButton.Click += async (s, e) => {
    var LocalPath = Path.Combine(AppContext.BaseDirectory, "index-5200-00.fits");
    if (!File.Exists(LocalPath)) {
        using (var fs = new FileStream(LocalPath, FileMode.Create, FileAccess.Write))
            using (var msg = await client.GetAsync(link1, cts.Token))
                await msg.Content.CopyToAsync(fs, cts.Token);
        Status = "Downloaded!";
    }
};

CancelButton.Click += (s, e) => {
    if (!cts.IsCancellationRequested) cts.Cancel();
};

Question is, how to properly handle the download cancellation?

Upvotes: 1

Views: 552

Answers (2)

Ladislav
Ladislav

Reputation: 329

I ended up with this code. It works exactly as expected:

var cts = new CancellationTokenSource();
var client = new HttpClient() { Timeout = TimeSpan.FromMinutes(5) };

DownloadButton.Click += async (s, e) => {
    var LocalPath = Path.Combine(AppContext.BaseDirectory, "index-5200-00.fits");
    if (!File.Exists(LocalPath)) {
        try {
            using (var fs = new FileStream(LocalPath, FileMode.Create, FileAccess.Write))
            using (var stream = await client.GetStreamAsync(link1, cts.Token))
                await stream.CopyToAsync(fs, cts.Token);
            Text = "Downloaded!";
        }
        catch (TaskCanceledException) {
            if (File.Exists(LocalPath)) File.Delete(LocalPath);
        }
        catch (Exception ex) {
            Text = ex.Message;
        }
    }
};

CancelButton.Click += (s, e) => {
    cts.Cancel();
};

Upvotes: 1

jmvcollaborator
jmvcollaborator

Reputation: 2455

Add a try before any async call first, then,

Be sure to register a callback: cancellationToken.Register(client.CancelAsync);

then when running the method await msg.Content.CopyToAsync(fs, cts.Token);

On the catch block you can handle the cancelation as follows:

catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled)
            {
                throw new OperationCanceledException();
            }               
            catch (TaskCanceledException)
            {
                throw new OperationCanceledException();
            }

Upvotes: 0

Related Questions