2014-12-31 14 views
22

Ich verwende die System.Net.Http.HttpClient, um einige clientseitige HTTP-Kommunikation zu tun. Ich habe das gesamte HTTP an einer Stelle, abstrahiert vom Rest des Codes. In einer Instanz möchte ich den Antwortinhalt als Stream lesen, aber der Konsument des Streams ist gut isoliert von dem Ort, an dem die HTTP-Kommunikation stattfindet und der Stream geöffnet wird. An dem für die HTTP-Kommunikation zuständigen Ort entsorge ich alle HttpClient Sachen.Wann oder wenn HttpResponseMessage beim Aufrufen von ReadAsStreamAsync zu disponentieren?

Diese Einheit Test wird bei Assert.IsTrue(stream.CanRead) fehlschlagen:

[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 
} 

Ich versuche normalerweise von etwas IDisposable zum frühestmöglichen Komfort zu entsorgen, aber in diesem Fall Entsorgung der HttpResponseMessage verfügt auch die Stream von ReadAsStreamAsync zurückgegeben.

So scheint es, als ob der aufrufende Code über die Antwortnachricht sowie den Stream wissen und die Verantwortung übernehmen muss, oder ich lasse die Antwortnachricht unangetastet und lasse den Finalizer damit umgehen. Keine der beiden Optionen fühlt sich richtig an.

This answer spricht über nicht die HttpClient entsorgen. Wie wäre es mit dem HttpRequestMessage und/oder HttpResponseMessage?

Bin ich etwas fehlt? Ich hoffe, dass der Verbrauchercode von HTTP keine Ahnung hat, aber all diese unangepassten Objekte zu lassen, geht gegen das Jahr der Gewohnheit!

+0

Nur ein Tipp - Nicht alles 'IDisposable' –

+0

entsorgt werden muss, scheint dies nichts mit' async' per se zu tun zu haben. Die Regeln sind auf beide Arten gleich: Entsorgen Sie das Objekt erst, wenn Sie damit fertig sind. Das gleiche gilt für die synchrone Version. Verwenden Sie also den zurückgegebenen 'Stream' _inside_ den' using'. Wenn Sie den "Stream" außerhalb des Kontexts verwenden müssen, in dem die Anforderung erstellt wurde, müssen Sie einen anderen Mechanismus einrichten, um ihn zum richtigen Zeitpunkt zu entfernen. –

+0

Auch: Ich empfehle nicht die Entsorgung für den Finalizer ...aber ich bemerke, dass du dich nicht darum kümmerst, 'client' zu verwerfen, also wenn du dich damit auskennst, kümmere dich auch nicht um die anderen Sachen. Beachten Sie, dass es sich bei der Antwort, die Sie referenzieren, um das Szenario handelt, in dem Sie das Objekt "HttpClient" _wiederverwenden; Offen gesagt sollte es offensichtlich sein, dass Sie es nicht wiederverwenden, wenn Sie es wiederverwenden wollen. Die Anleitung sagt überhaupt nichts darüber aus, ob es legitim ist, die Objektentsorgung nur durch Finalisierung passieren zu lassen (und IMHO ist das eine sehr schlechte Form). –

Antwort

8

So scheint es, wie der aufrufende Code kennen muss und Eigentum der Antwortnachricht sowie den Strom nehmen, oder ich lasse die Antwortnachricht undisposed und die Finalizerthread mit ihnen umgehen lassen. Keine der Optionen fühlt sich richtig an.

In diesem speziellen Fall gibt es keine Finalizer. Weder HttpResponseMessage noch HttpRequestMessage implementieren einen Finalizer (und das ist eine gute Sache!). Wenn Sie keinen von beiden entsorgen, werden sie Garbage Collected erhalten, sobald der GC startet, und der Handle zu ihren zugrunde liegenden Streams wird gesammelt, sobald dies passiert.

Solange Sie diese Objekte verwenden, verfügen nicht. Einmal fertig, entsorgen sie. Statt sie in eine using-Anweisung einzufügen, können Sie immer explizit Dispose aufrufen, sobald Sie fertig sind. In beiden Fällen muss der konsumierende Code kein Wissen besitzen, das HTTP-Anfragen zugrunde liegt.

+0

Das ist überhaupt keine gute Sache! Das Zurücklassen nicht entsorgter HttpMessageRequest- und HttpMessageResponse-Objekte bedeutet, dass sie nur von GC entsorgt werden (wann immer sie sich dazu entscheiden, ausgeführt zu werden), und bis dann alle für den Aufruf verwendeten Ressourcen freigegeben werden. Dies führt zu Port-Erschöpfung, Timeouts, wenn gleichzeitige HTTP-Anfragen begrenzt sind, usw. ... –

+0

@AlonCatz Wo in meiner Antwort habe ich gesagt, die Objekte nicht zu entsorgen? Hast du den zweiten Absatz gelesen? –

+0

@AlonCatz - Der GC ** ruft niemals '.Dispose()' auf. Nur wenn es einen Finalizer gibt, der '.Dispose()' aufruft, kann der GC eine Entsorgung auslösen. – Enigmativity

1

Sie können den Stream auch als Eingabeparameter verwenden, damit der Aufrufer die vollständige Kontrolle über den Typ des Streams hat und verfügbar ist. Und jetzt können Sie auch httpResponse absetzen, bevor die Kontrolle die Methode verlässt.
Unten ist die Erweiterungsmethode für 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);     
     } 
    }