2014-12-31 13 views
22

Sto usando il System.Net.Http.HttpClient di fare qualche comunicazione HTTP sul lato client. Ho tutto il HTTP in un punto, astratto rispetto al resto del codice. In un'istanza voglio leggere il contenuto della risposta come uno stream, ma il consumatore dello stream è ben isolato da dove avviene la comunicazione HTTP e lo stream è aperto. Nel posto responsabile della comunicazione HTTP, sto eliminando tutto il materiale HttpClient.Quando o se eliminare HttpResponseMessage quando si chiama ReadAsStreamAsync?

prova

Questa unità non riuscirà a Assert.IsTrue(stream.CanRead):

[TestMethod] 
public async Task DebugStreamedContent() 
{ 
    Stream stream = null; // in real life the consumer of the stream is far away 
    var client = new HttpClient();   
    client.BaseAddress = new Uri("https://www.google.com/", UriKind.Absolute); 

    using (var request = new HttpRequestMessage(HttpMethod.Get, "/")) 
    using (var response = await client.SendAsync(request)) 
    { 
     response.EnsureSuccessStatusCode(); 
     //here I would return the stream to the caller 
     stream = await response.Content.ReadAsStreamAsync(); 
    } 

    Assert.IsTrue(stream.CanRead); // FAIL if response is disposed so is the stream 
} 

Io di solito cerco di disporre di tutto ciò IDisposable al più presto possibile, ma in questo caso, disporre il HttpResponseMessage dispone inoltre il Stream tornato da ReadAsStreamAsync.

Quindi sembra che il codice chiamante debba conoscere e assumere la proprietà del messaggio di risposta e del flusso, oppure lasciare il messaggio di risposta non esposto e lasciare che il finalizzatore lo gestisca. Nessuna delle due opzioni sembra giusta.

This answer parla della mancata disponibilità di HttpClient. Che ne dite di HttpRequestMessage e/o HttpResponseMessage?

mi sto perdendo qualcosa? Spero di mantenere il codice che consuma ignorante di HTTP, ma lasciare tutti questi oggetti non disposti in giro va contro l'anno di abitudine!

+0

Solo un consiglio - Non tutto 'IDisposable' da smaltire –

+0

questo non sembra avere nulla a che fare con' async' di per sé. Le regole sono le stesse in ogni caso: non disporre l'oggetto finché non hai finito. La stessa cosa si applicherebbe usando la versione sincrona. Quindi, usa il 'stream' _inside_ restituito l'' using'. Se è necessario utilizzare il 'Stream' al di fuori del contesto in cui viene creata la richiesta, sarà necessario impostare un meccanismo diverso per eliminarlo al momento giusto. –

+0

Inoltre: non è consigliabile lasciare lo smaltimento per il finalizzatore ...ma noto che non ti preoccupi di eliminare il client in ogni caso, quindi se ti senti a tuo agio, non preoccuparti nemmeno delle altre cose. Per quanto riguarda la risposta a cui si fa riferimento, si noti che si riferisce allo scenario in cui si _retra_ l'oggetto 'HttpClient'; francamente, dovrebbe essere ovvio che se si vuole riutilizzarlo, non lo si può eliminare. La guida non dice nulla sul fatto che sia legittimo lasciare che lo smaltimento degli oggetti avvenga tramite la finalizzazione (e IMHO è una forma molto povera). –

risposta

8

Così sembra che il codice chiamante ha bisogno di conoscere e prendere proprietà del messaggio di risposta così come il flusso, o lascio il messaggio di risposta undisposed e lasciare che l'affare finalizzatore con esso. Nessuna opzione sembra giusta.

In questo caso specifico, non ci sono finalizzatori. Né HttpResponseMessage o HttpRequestMessage implementare un finalizzatore (e questa è una buona cosa!). Se non si dispone di nessuno dei due, otterranno i garbage collection una volta che il GC sarà entrato in gioco e l'handle dei loro flussi sottostanti verrà raccolto una volta che ciò accadrà.

Finché si sta utilizzando questi oggetti, non smaltire. Al termine, smaltire. Invece di inserirli in un'istruzione using, puoi sempre chiamare esplicitamente Dispose una volta terminato. In entrambi i casi, il codice di consumo non deve avere alcuna conoscenza delle richieste HTTP.

+0

Questa non è una buona cosa! Lasciare gli oggetti HttpMessageRequest e HttpMessageResponse non disposti significa che saranno eliminati solo da GC (ogni volta che potrebbe decidere di eseguire) e fino a quel momento tutte le risorse utilizzate per la chiamata verranno liberate. Questo porta all'esaurimento delle porte, ai timeout se c'è un limite alle richieste HTTP simultanee, ecc ... –

+0

@AlonCatz Dove nella mia risposta ho detto di non disporre gli oggetti? Hai letto il secondo paragrafo? –

+0

@AlonCatz - GC ** never ** chiama '.Dispose()' a sé stante. È solo se c'è un finalizzatore che chiama '.Dispose()' che il GC può attivare un dispose. – Enigmativity

1

È anche possibile eseguire lo streaming come parametro di input, in modo che il chiamante abbia il controllo completo sul tipo di streaming e sul suo smaltimento. E ora puoi anche eliminare httpResponse prima che il controllo lasci il metodo.
Di seguito è il metodo di estensione per HttpClient

public static async Task HttpDownloadStreamAsync(this HttpClient httpClient, string url, Stream output) 
    { 
     using (var httpResponse = await httpClient.GetAsync(url).ConfigureAwait(false)) 
     { 
      // Ensures OK status 
      response.EnsureSuccessStatusCode(); 

      // Get response stream 
      var result = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); 

      await result.CopyToAsync(output).ConfigureAwait(false); 
      output.Seek(0L, SeekOrigin.Begin);     
     } 
    } 
Problemi correlati