Исключение периодического тайм-аута, когда я отменяю задачу 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