web-dev-qa-db-ja.com

ReadAsStreamAsyncを呼び出すときにHttpResponseMessageを破棄するタイミングまたはタイミングは?

_System.Net.Http.HttpClient_ を使用して、クライアント側のHTTP通信を実行しています。すべてのHTTPを1か所にまとめ、残りのコードから抽象化しました。 1つの例では、応答コンテンツをストリームとして読み取りたいが、ストリームのコンシューマーは、HTTP通信が発生してストリームが開かれる場所から十分に隔離されています。 HTTP通信を担当するスポットでは、HttpClientをすべて破棄しています。

この単体テストは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
}
_

私は通常、IDisposableをできるだけ早く破棄しようとしますが、この場合、HttpResponseMessageを破棄すると、Streamから返されたReadAsStreamAsyncも破棄します。

したがって、呼び出しコードは、応答メッセージとストリームの所有権を把握し、所有権を取得する必要があるようです。または、応答メッセージを破棄せずにファイナライザーに処理させます。どちらのオプションも適切ではありません。

この回答HttpClientを破棄しないことについて説明しています。 HttpRequestMessageHttpResponseMessageはどうですか?

何か不足していますか?私は、消費するコードをHTTPに無知のままにしておきたいと思っていますが、これらすべての無秩序なオブジェクトを残すことは、習慣の年に反します!

33
dkackman

したがって、呼び出しコードは、応答メッセージとストリームの所有権を把握し、所有権を取得する必要があるようです。または、応答メッセージを破棄せずにファイナライザーに処理させます。どちらのオプションも適切ではありません。

この特定の場合、-ファイナライザーはありませんHttpResponseMessageHttpRequestMessageもファイナライザーを実装していません(それは良いことです!)。どちらも廃棄しない場合は、GCが開始されるとガベージコレクションが行われ、それが発生すると、基になるストリームのハンドルが収集されます。

これらのオブジェクトを使用している限り、廃棄しないでください。完了したら、それらを破棄。それらをusingステートメントでラップする代わりに、完了したらいつでも明示的にDisposeを呼び出すことができます。どちらにしても、消費するコードはhttpリクエストの基礎となる知識を持っている必要はありません。

11
Yuval Itzchakov

ストリームを入力パラメーターとして使用することもできます。そのため、呼び出し側は、ストリームの種類と処理方法を完全に制御できます。また、制御がメソッドを終了する前にhttpResponseを破棄することもできます。
以下は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);                
        }
    }
7
LP13

.NETでのDisposesの処理は簡単であり、かつ困難です。確かに。

ストリームはこれと同じナンセンスを引き出します...バッファを破棄すると、ラップされたストリームも自動的に破棄されますか?すべきですか?消費者として、それがあるかどうかを知る必要がありますか?

私がこのようなものを扱うとき、私はいくつかのルールに従っています:

  1. プレイ中にネイティブでないリソース(ネットワーク接続など)があると思う場合、GCに「それを回避」させないようにします。リソースの枯渇は現実的であり、適切なコードがそれを処理します。
  2. DisposableがDisposableをパラメーターとして使用する場合、お尻を覆い、コードが作成するすべてのオブジェクトを廃棄することを確認しても害はありません。私のコードがうまくいかなかった場合、私はそれを無視することができます。
  3. GCは〜Finalizeを呼び出しますが、Finalize(つまり、カスタムデストラクタ)がDisposeを呼び出すことを保証するものはありません。上記の意見に反して魔法はないので、あなたはそれに対して責任を負わなければなりません。

したがって、HttpClient、HttpRequestMessage、およびHttpResponseMessageがあります。それらのそれぞれの寿命、およびそれらが作るあらゆる使い捨て品は尊重されなければなりません。したがって、yoはStreamをインスタンス化しないため、HttpResponseMessageのDispoableライフタイム以外では、Streamが存続することは期待できません。

あなたの上記のシナリオでは、私のパターンは、そのStreamを取得することが実際にStatic.DoGet(uri)メソッドにあるふりをすることであり、返されるStreamは私たち自身のものの1つになります。つまり、HttpResponseMessageのストリームが.CopyTo'dされた新しいストリーム(FileStream、MemoryStream、または状況に最も適したもの)を含む2番目のStream ...またはそれに類似したものを意味します。なぜなら:

  • HttpResponseMessageのストリームの存続期間に対する権利はありません。それは彼のものであり、あなたのものではありません。 :)
  • HttpClientのような使い捨てのライフタイムを保持しながら、返されたストリームのコンテンツを処理することは、非常に難しいブロッカーです。これは、DataTableの解析中にSqlConnectionを保持するようなものです(DataTablesが巨大になった場合に接続プールがどれほど速く枯渇するかを想像してください)
  • 応答を取得するhowを公開すると、SOLIDに対して機能する可能性があります...ストリームは使い捨てでしたが、それはHttpResponseMessageからのもので、使い捨てでしたが、HttpClientとHttpRequestMessageは使い捨てです...そして、必要なのはURIからのストリームだけでした。これらの責任はどのように混乱しますか?
  • ネットワークは依然としてコンピューターシステムの中で最も遅いレーンです。 「最適化」のためにそれらを保持することは依然として正気ではありません。最も遅いコンポーネントを処理するより良い方法が常にあります。

したがって、キャッチアンドリリースなどの使い捨てを使用してください...それらを作成し、自分で結果をひっかかり、できるだけ早くそれらをリリースしてください。また、特に自分で作成しなかったクラスからの最適化を正確性と混同しないでください。

6
Craig Brunetti