Не удается перехватить исключения, выбрасываемые внутренними функциями.

Вопрос или проблема

Я использовал IProgress и Task для асинхронной загрузки и отображения процесса загрузки, поддерживая асинхронное отмену.
Создайте внутреннюю функцию UploadFileViaSftp и вызывайте ее во внешней функции через await Task.Run(() => UploadFileViaSftp(…)). Внешняя функция уже имеет блок catch, но ошибка OperationCanceledException все равно возникает.

private async void btn_save_Click(object sender, EventArgs e)
{
    try
    {
         cancellationTokenSource = new CancellationTokenSource();
         var token = cancellationTokenSource.Token;
         progressForm = new ProgressForm(() =>
         {
             cancellationTokenSource.Cancel();

         });
         progressForm.Show();                              
         var progressIndicator = new Progress<int>(percent =>
        {                                  
         progressForm.UpdateProgress(percent);
        });
        await Task.Run(() => UploadFileViaSftp(client, token, progressIndicator, fileStream, pictureStream, remoteDirectory + "/" + fileName));

        MessageBox.Show("успех");
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show("отменено");
    }
    catch (Exception ex)
    {
        MessageBox.Show("ошибка" + ex.Message);
    }
    finally
    {
            }
}
        private void UploadFileViaSftp(SftpClient sftpClient, CancellationToken cancellationToken, IProgress<int> progress, FileStream fileStream, FileStream picStream, string remoteFilePath)
        {
            bool isFileStartUploaded = false;
            bool isPicStartUploaded = false;
            try
            {
                if (!sftpClient.IsConnected)
                {
                    sftpClient.Connect();
                }

                if (fileStream.CanRead && picStream.CanRead)
                {
                    long totalBytes = fileStream.Length + picStream.Length;
                    long uploadedBytes = 0;

                    byte[] buffer = new byte[8192];
                    int bytesRead;

                    using (var uploadStream = sftpClient.OpenWrite(remoteFilePath))
                    {
                        isFileStartUploaded = true;
                        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            
                            cancellationToken.ThrowIfCancellationRequested();

                            uploadStream.Write(buffer, 0, bytesRead);
                            uploadedBytes += bytesRead;

                            int percentComplete = (int)((double)uploadedBytes / totalBytes * 100);

                            
                            progress.Report(percentComplete);
                        }
                    }

                    using (var uploadPicStream = sftpClient.OpenWrite(Path.ChangeExtension(remoteFilePath, ".png")))
                    {
                        isPicStartUploaded = true;
                        while ((bytesRead = picStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            cancellationToken.ThrowIfCancellationRequested();

                            uploadPicStream.Write(buffer, 0, bytesRead);
                            uploadedBytes += bytesRead;

                            int percentComplete = (int)((double)uploadedBytes / totalBytes * 100);

                            
                            progress.Report(percentComplete);
                        }
                    }
                    isPicStartUploaded = true;
                }
                else
                {
                    MessageBox.Show("доступ запрещен");
                }
            }
            catch (OperationCanceledException)
            {
                if (!sftpClient.IsConnected)
                {
                    sftpClient.Connect();
                }

               
                if (isFileStartUploaded && sftpClient.Exists(remoteFilePath))
                {
                    sftpClient.DeleteFile(remoteFilePath);
                }

               
                string remotePicPath = Path.ChangeExtension(remoteFilePath, ".png");
                if (isPicStartUploaded && sftpClient.Exists(remotePicPath))
                {
                    sftpClient.DeleteFile(remotePicPath);
                }
                string remoteDirectory = Path.GetDirectoryName(remoteFilePath);
                if (sftpClient.Exists(remoteDirectory) && sftpClient.ListDirectory(remoteDirectory).Count() == 0)
                {
                    sftpClient.DeleteDirectory(remoteDirectory);
                }
                throw;
            }
            catch (Exception)
            {
                throw;
            }
        }

Что я могу сделать, когда внутренняя функция UploadFileViaSftp перехватывает OperationCancelException и бросает ее в внешнюю функцию снова для дальнейшей обработки

Ответ или решение

Когда вы работаете с асинхронными задачами и методами, такими как UploadFileViaSftp, важно правильно обрабатывать исключения, чтобы они могли быть пойманы в верхнем уровне вызова. В вашем случае исключение OperationCanceledException не обрабатывается корректно из-за того, что оно перехватывается внутри функции UploadFileViaSftp и выбрасывается снова, но при этом может не обрабатываться должным образом, если не учесть контекст вызова. Для решения этой проблемы необходимо внести изменения в код, чтобы исключение обрабатывалось правильно.

Вот несколько шагов, которые помогут решить вашу проблему:

  1. Удалите
    catch (OperationCanceledException) в UploadFileViaSftp
    , если вы планируете обработать это исключение в вызывающем методе. В этом случае просто выбросьте исключение дальше без обработки его в текущем методе.

  2. Используйте правильный подход для завершения операций, когда производится отмена. Если отмена вызвана, с помощью токена отмены вы можете просто выбросить это исключение.

  3. Убедитесь, что вы правильно выбрасываете исключение в UploadFileViaSftp, если происходит отмена, и не обрабатываете OperationCanceledException, так как оно должно быть обработано в основном методе.

Вот обновлённая версия вашего кода:

private async void btn_save_Click(object sender, EventArgs e)
{
    try
    {
        cancellationTokenSource = new CancellationTokenSource();
        var token = cancellationTokenSource.Token;
        progressForm = new ProgressForm(() =>
        {
            cancellationTokenSource.Cancel();
        });
        progressForm.Show();
        var progressIndicator = new Progress<int>(percent =>
        {
            progressForm.UpdateProgress(percent);
        });

        // Вызов функции UploadFileViaSftp через Task.Run
        await Task.Run(() => UploadFileViaSftp(client, token, progressIndicator, fileStream, pictureStream, remoteDirectory + "/" + fileName));

        MessageBox.Show("Удаление завершено успешно");
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show("Операция отменена");
    }
    catch (Exception ex)
    {
        MessageBox.Show("Ошибка: " + ex.Message);
    }
    finally
    {
        // Освобождение ресурсов, если требуется
    }
}

private void UploadFileViaSftp(SftpClient sftpClient, CancellationToken cancellationToken, IProgress<int> progress, FileStream fileStream, FileStream picStream, string remoteFilePath)
{
    bool isFileStartUploaded = false;
    bool isPicStartUploaded = false;

    if (!sftpClient.IsConnected)
    {
        sftpClient.Connect();
    }

    if (fileStream.CanRead && picStream.CanRead)
    {
        long totalBytes = fileStream.Length + picStream.Length;
        long uploadedBytes = 0;

        byte[] buffer = new byte[8192];
        int bytesRead;

        using (var uploadStream = sftpClient.OpenWrite(remoteFilePath))
        {
            isFileStartUploaded = true;
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                cancellationToken.ThrowIfCancellationRequested();

                uploadStream.Write(buffer, 0, bytesRead);
                uploadedBytes += bytesRead;

                int percentComplete = (int)((double)uploadedBytes / totalBytes * 100);
                progress.Report(percentComplete);
            }
        }

        using (var uploadPicStream = sftpClient.OpenWrite(Path.ChangeExtension(remoteFilePath, ".png")))
        {
            isPicStartUploaded = true;
            while ((bytesRead = picStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                cancellationToken.ThrowIfCancellationRequested();

                uploadPicStream.Write(buffer, 0, bytesRead);
                uploadedBytes += bytesRead;

                int percentComplete = (int)((double)uploadedBytes / totalBytes * 100);
                progress.Report(percentComplete);
            }
        }
    }
    else
    {
        MessageBox.Show("Доступ к файлам запрещен");
    }
}

В этом коде:

  • OperationCanceledException не обрабатывается в UploadFileViaSftp, что позволяет ему быть пойманным в методе btn_save_Click и соответствующим образом отреагировать на отмену операции.
  • Убедитесь, что блоки кода, отвечающие за удаление файлов и очистку, находятся в блоке finally в методе btn_save_Click или в самом методе UploadFileViaSftp, если это необходимо.

Таким образом, вы обеспечите слежение за состоянием при отмене и корректное реагирование наCancellation.

Оцените материал
Добавить комментарий

Капча загружается...