2012-10-01 15 views
24

HttpClient ma wbudowaną funkcję limitu czasu (mimo że wszystkie są asynchroniczne, tj. Limity czasu mogą być traktowane jako ortogonalne w stosunku do funkcji żądania http, a zatem obsługiwane przez ogólne asynchroniczne narzędzia, ale na bok) i gdy upłynie limit czasu oczekiwania , rzuci TaskCanceledException (owinięty w AggregateException).Rozróżnienie limitu czasu od anulowania przez użytkownika

TCE zawiera CancellationToken, który jest równy CancellationToken.None.

Teraz gdybym zapewnić HttpClient z CancellationToken z własną rękę i używać, aby anulować operację przed jej zakończeniem (lub limit czasu), mam dokładnie to samo TaskCanceledException znowu z CancellationToken.None.

Czy nadal istnieje sposób, patrząc tylko na wyjątek rzucony, aby dowiedzieć się, czy limit czasu anulowania żądania, bez konieczności zrobić własny CancellationToken dostęp do kodu, który sprawdza wyjątek?

P.S. Czy to może być błąd, a CancellationToken został nieprawidłowo naprawiony na CancellationToken.None? W przypadku anulowanej przy użyciu niestandardowego przypadku CancellationToken, oczekiwałbym, że TaskCanceledException.CancellationToken będzie taki sam, jak token niestandardowy.

Edit Aby ten problem nieco bardziej jasne, z dostępem do pierwotnego CancellationTokenSource, to jest łatwe do odróżnienia limitu czasu i obsługi rezerwacji:

origCancellationTokenSource.IsCancellationRequested == prawdziwą

Otrzymanie CancellationToken z wyjątku, ale daje błędną odpowiedź:

((TaskCanceledException) e.InnerException) .CancellationToken.IsCancellationRequested == fałszywy

Tutaj minimalny przykład, z uwagi na duże zainteresowanie:

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); 
} 
+0

proszę umieszczać minimalny fragment kodu, który wykazuje takie zachowanie. –

+0

dlaczego kod nie uruchamia zadania obsługującego wyjątek?Ten fragment kodu powinien już mieć Token Anulowania, może zarządzać tym scenariuszem, a następnie po prostu wyrzucić wyjątek, który ma otrzymać blok próbny/catch wyższego poziomu, bez TokenSource –

+1

@FranciscoNoriega Czasami chcesz oddzielić kod, np. aby był możliwy do ponownego użycia, a także w asynchronicznie wykonanym kodzie. W efekcie wychwytywanie wyjątków odbywa się w różnych miejscach, niż tam, gdzie został zdefiniowany kod. Nie chcę przechodzić przez cały stan, który jest zaangażowany, zwłaszcza, że ​​wyjątek zdaje się zapewniać ten stan, kiedy jest potrzebny - z tym wyjątkiem, że nie działa tak, jak sądzę, że powinien. Rzuć okiem na przykład, który z grubsza pokazuje, jak go używam. –

Odpowiedz

4

Tak, oba zwracają ten sam wyjątek (prawdopodobnie z powodu timeoutu wewnętrznie za pomocą tokena), ale można to łatwo ustalić, wykonując to:

catch (OperationCanceledException ex) 
      { 
       if (token.IsCancellationRequested) 
       { 
        return -1; 
       } 

       return -2; 
      } 

więc w zasadzie, jeśli trafisz wyjątek ale token nie został odwołany, a to był regularny Timeout http

+0

Zrobiłem **, patrząc tylko na wyjątek wyrzucony ** w moim pytaniu pogrubionym i dodałem trochę więcej szczegółów, aby było jasne, o co proszę. –

+0

'OperationCanceledException' jest również generowany, jeśli' HttpClient' zostanie usunięty, gdy żądanie oczekuje, lub gdy zostanie wywołane 'HttpClient.CancelPendingRequests'. Przypadki te można odróżnić, jeśli kontrolujesz wywołania tych metod. – Ernesto

+0

w tej odpowiedzi brakuje szczegółów ... skąd pochodzi token? czy jest to właściwość wyjątku? downvoting, dopóki go nie poprawisz – ympostor

5

Zaakceptowanych odpowiedź jest z pewnością jak to powinien praca w teorii, ale niestety w praktyce IsCancellationRequested robi nie (rzetelnie) gotowi na tokena, który jest dołączony do wyjątkiem:

Cancelling an HttpClient Request - Why is TaskCanceledException.CancellationToken.IsCancellationRequested false?

+0

może nie być możliwe używanie załączonego tokena, ale jeśli masz oryginalny token (który został przekazany do 'SendAsync'), możesz go użyć. Czy może czegoś brakuje? –

+0

Rozumiem, brakuje mi tego, że pytanie wymagało polegania wyłącznie na danych wyjątku. –

Powiązane problemy