Исключение периодического тайм-аута, когда я отменяю задачу OneDrive, которая считывает информацию о файле

Я использую Microsoft Graph C# SDK для управления файлами OneDrive. Мой код работает с хранилищем параллельно (максимальное количество потоков < 6). Для отмены недопустимой операции (пользователь закрывает окно или просматривает другую папку), я использую CancellationToken,

Время от времени я получаю Microsoft.Graph.ServiceException с кодом Error.Code = GraphErrorCode.Timeout когда отменяются задачи. Это должно быть OperationCanceledException,

Как это исправить?

Код тривиален, поэтому я думаю, что это ошибка в Microsoft Graph. Есть много похожих вопросов по Stackru.

Версия SDK есть 1.7.0 (та же ошибка существует в 1.5.0 также).

Обновить:

  • Было неверным предположение, что эта ошибка связана с другими подобными вопросами в Stackru.
  • Я создал ошибку на MSGraph GitHub.

2 ответа

Решение

Я думаю, что нашел эту ошибку в Microsoft Graph C# SDK.

Пожалуйста, посмотрите в реализации HttpProvider.SendRequestAsync метод.
Он содержит следующий код:

    internal async Task<HttpResponseMessage> SendRequestAsync(
        HttpRequestMessage request,
        HttpCompletionOption completionOption,
        CancellationToken cancellationToken)
    {
        try
        {
            return await this.httpClient.SendAsync(request, completionOption, cancellationToken).ConfigureAwait(false);
        }
        catch (TaskCanceledException exception)
        {
            throw new ServiceException(
                    new Error
                    {
                        Code = ErrorConstants.Codes.Timeout,
                        Message = ErrorConstants.Messages.RequestTimedOut,
                    },
                    exception);
        }
        catch (Exception exception)
        {
            throw new ServiceException(
                    new Error
                    {
                        Code = ErrorConstants.Codes.GeneralException,
                        Message = ErrorConstants.Messages.UnexpectedExceptionOnSend,
                    },
                    exception);
        }
    }

Итак, как вы видите, это превращается TaskCanceledException в ServiceException с Code = ErrorConstants.Codes.Timeout, TaskCanceledException исключение, которое выдает.NET 4.5 при отмене задачи (CancellationToken отмечен для отмены задания). Этот класс исключений наследуется от OperationCanceledException,

У меня только один вопрос к MS - ПОЧЕМУ!???
Во многих местах MSDN есть фразы о OperationCanceledException является важным исключением, он используется кодом управления задачами.NET для проверки причины отмены и переключения задачи в отмененное состояние. Рабочий метод задания не должен блокировать это исключение! Если вы анализируете это исключение в своем коде задачи, вы должны повторно выбросить его для распространения. В противном случае задача будет завершена в неудачном состоянии. Только код, который запускает задачу и ожидает ее завершения, может перехватить это исключение и проанализировать его. Пожалуйста, прочтите - "Параллельные задачи", раздел "Отмена задачи"

Если токен отмены указывает, что отменено было запрошено, метод ThrowIfCancellationRequested создает экземпляр OperationCanceledException и передает токен отмены. Затем он выбрасывает исключение. Это исключение является сигналом, который уведомляет.NET Framework о том, что задача была отменена; поэтому исключение OperationCanceledException не должно обрабатываться кодом пользователя в задаче (однако, оно часто обрабатывается вне задачи, которая была отменена). Если вы выполните действия, описанные в этом разделе, задача будет остановлена, а ее свойство Status будет установлено в перечисляемое значение TaskStatus.Canceled.

Пожалуйста, удалите catch (TaskCanceledException exception),

Обновление: Обходной путь в моем коде для исправления этой ошибки:

    // This method creates Task (because it's asynchronous) that can be canceled by token
    public async Task<DriveItem> MyUsefulMethodAsync(string driveId, string itemId, string selectItemParameter,  CancellationToken? cancellationToken = null)
    {
        DriveItem res = default(DriveItem);

        try
        {
            // My useful code, for example:
            res = await Client.Drives[driveId].Items[itemId].Request()
                    .Select(selectItemParameter)
                    .GetAsync(cancellationToken ?? CancellationToken.None);
        }
        catch (OperationCanceledException)
        {
            // We have to propagate out this exception for fine Task state managing.
            throw;
        }
        catch (Exception e)
        {
            // This is workaround for MSGraph bug (https://stackru.com/a/47900445/987850)
            var se = e as ServiceException;
            if (se?.InnerException is OperationCanceledException)
            {
                throw se.InnerException;
            }

            // My error processing ...
        }

        return res;
    }

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

    try
    {
        // cancellation token from token source (can be invoked to cancel state by UI handlers)
        var cancellationToken = m_bkCancelTokenSource.Token;

        var driveItem = await MyUsefulMethodAsync(driveId, itemId, "id, name, parentReference, file, remoteItem, size", cancellationToken);
        if (driveItem!=null)
        {
            // some code ...
        }
    }
    catch(OperationCanceledException)
    {
        // task was canceled by user
    }

Эта проблема будет исправлена ​​в следующем выпуске 2.0 основной библиотеки Microsoft. См. Здесь https://github.com/microsoftgraph/msgraph-sdk-dotnet-core/pull/109

Я не уверен, где вы видите это документально, что вы должны получить OperationCanceledException, В SDK нет методов, генерирующих исключения такого типа.

Для GraphErrorCode.Timeoutэто скорее всего означает, что запрос вернул HTTP 504 Gateway Timeout, Это довольно общая ошибка, которая просто означает, что что-то в цепочке вызовов не смогло вернуть результат вовремя. Согласно документации:

Возобновите или повторите загрузку, которая не удалась из-за прерывания соединения или любого другого 5xx ошибки, в том числе:

  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout
Другие вопросы по тегам