WebException при вызове WebClient.CancelAsync на «ожидание DownloadTaskAsync»

Это отлично работает:

private WebClient _webClient;

private void ButtonStart_Click(object sender, RoutedEventArgs e) {
    using (_webClient = new WebClient()) {
        _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
}

private void ButtonStop_Click(object sender, RoutedEventArgs e) {
    _webClient.CancelAsync();
}

Хотя этот код (обратите внимание на шаблон async/await)...:

private WebClient _webClient;

private async void ButtonStart_Click(object sender, RoutedEventArgs e) {
    using (_webClient = new WebClient()) {
        await _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
}

private void ButtonStop_Click(object sender, RoutedEventArgs e) {
    _webClient.CancelAsync();
}

... выдает следующее исключение:

System.Net.WebException

The request was aborted: The request was canceled.

   at System.Net.ConnectStream.EndRead(IAsyncResult asyncResult)
   at System.Net.WebClient.DownloadBitsReadCallbackState(DownloadBitsState state, IAsyncResult result)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at WpfApp1.MainWindow.<ButtonStart_Click>d__2.MoveNext() in WpfApp1\MainWindow.xaml.cs:line 19

Как я могу отменить задачу, запущенную с помощью await WebClient.DownloadFileTaskAsync(), не вызывая исключения?

Как узнать, что загрузка была прервана?

Paulo Morgado 26.04.2019 21:23

Я сомневаюсь, что первый блок кода действительно работает нормально: вы будете удалять _webClient почти мгновенно, что означает, что он, вероятно, будет удален к тому времени, когда CancelAsync() будет вызван. Я предполагаю, что CancelAsync возвращает ошибку в задаче, которую вы игнорируете, потому что не ожидаете ее. Таким образом, он работает нормально, но на самом деле запрос не отменяется.

StriplingWarrior 26.04.2019 22:55

@PauloMorgado Путем проверки статуса AsyncCompletedEventArgs.Cancelled в обработчике событий WebClient.DownloadFileCompleted.

Otiel 29.04.2019 09:12

@StriplingWarrior Ну, загрузка остановлена, это наверняка означает, что запрос отменен, не так ли?

Otiel 29.04.2019 09:16

@Отиэль: Туше. Я просто подумал, что ваша загрузка не должна завершиться, когда запрос не отменен. Обычно, если у вас есть оператор using и вы пренебрегаете ожиданием асинхронных задач, вы обожжетесь за это. Но, основываясь на этот комментарий и исходный код, WebClient в основном игнорирует удаление.

StriplingWarrior 29.04.2019 17:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
242
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Исключением является именно то, как работает предполагаемый.

Если вы не хотите, чтобы это исключение распространялось из вашего обработчика событий, перехватите это исключение.

Вы можете поймать исключение следующим образом:

using (_webClient = new WebClient())
{
    try
    {
        await _webClient.DownloadFileTaskAsync("https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin");
    }
    catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled)
    {
        Console.WriteLine("Cancelled");
    }
}

Обновлять: Как изменить поведение CancelAsync по умолчанию, чтобы не перехватывать исключение:

public static Task<bool> OnCancelReturnTrue(this Task task)
{
    return task.ContinueWith(t =>
    {
        if (t.IsFaulted)
        {
            if (t.Exception.InnerException is WebException webEx
                && webEx.Status == WebExceptionStatus.RequestCanceled) return true;
            throw t.Exception;
        }
        return t.IsCanceled;
    }, TaskContinuationOptions.ExecuteSynchronously);
}

Пример использования:

bool cancelled = await _webClient.DownloadFileTaskAsync(
    "https://speed.hetzner.de/100MB.bin", @"D:\100MB.bin").OnCancelReturnTrue();
if (cancelled) Console.WriteLine("Cancelled");

Спасибо. Это уже то, что я делаю, просто хотел узнать, есть ли более чистый способ отменить задачу, не вызывая исключения.

Otiel 29.04.2019 09:10

@Otiel выдает исключение по дизайну. Видимо отмена воспринимается как нечто исключительное. Если вы хотите пофантазировать, вы можете придумать какой-нибудь собственный метод расширения, чтобы изменить это поведение по своему вкусу. Я обновил свой ответ примером.

Theodor Zoulias 29.04.2019 11:32

Другие вопросы по теме