HttpClient
имеет встроенную функцию тайм-аута (несмотря на то, что все они асинхронны, т.е. тайм-ауты могут считаться ортогональными по отношению к функциям http-запроса и, таким образом, обрабатываются общими асинхронными утилитами, но это в стороне), и когда тайм-аут срабатывает, он выдает TaskCanceledException
( завернутый в AggregateException
).
TCE содержит CancellationToken
, равное CancellationToken.None
.
Теперь, если я предоставлю HttpClient
свой собственный CancellationToken
и использую его для отмены операции до ее завершения (или истечения времени ожидания), я получу точно такой же TaskCanceledException
, снова с CancellationToken.None
.
Есть ли еще способ, посмотрев только на выброшенное исключение, чтобы выяснить, отменил ли запрос тайм-аут, без необходимости делать мой собственный CancellationToken
доступным для кода, проверяющего исключение?
P.S. Может ли это быть ошибкой, и CancellationToken
каким-то образом неправильно исправлено в CancellationToken.None
? В случае отменено с использованием пользовательского CancellationToken я ожидаю, что TaskCanceledException.CancellationToken
будет равно этому пользовательскому токену.
Изменить. Чтобы сделать проблему немного более ясной, имея доступ к исходному CancellationTokenSource
, легко отличить время ожидания от отмены пользователя:
origCancellationTokenSource.IsCancellationRequested == true
Однако получение CancellationToken
из исключения дает неправильный ответ:
((TaskCanceledException) e.InnerException).CancellationToken.IsCancellationRequested == false
Вот минимальный пример по многочисленным просьбам:
public void foo()
{
makeRequest().ContinueWith(task =>
{
try
{
var result = task.Result;
// do something with the result;
}
catch (Exception e)
{
TaskCanceledException innerException = e.InnerException as TaskCanceledException;
bool timedOut = innerException != null && innerException.CancellationToken.IsCancellationRequested == false;
// Unfortunately, the above .IsCancellationRequested
// is always false, no matter if the request was
// cancelled using CancellationTaskSource.Cancel()
// or if it timed out
}
});
}
public Task<HttpResponseMessage> makeRequest()
{
var cts = new CancellationTokenSource();
HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10) };
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "url");
passCancellationTokenToOtherPartOfTheCode(cts);
return client.SendAsync(httpRequestMessage, cts.Token);
}
MyTaskCanceledException
с флагом. Затем вы перехватываете исходныйTaskCanceledException
в области, где у вас есть доступ к исходномуCancellationTokenSource
, и переупаковываете его в собственное исключение, устанавливая флаг с помощьюorigCancellationTokenSource.IsCancellationRequested
. - person Evgeniy Berezovsky   schedule 29.11.2013