_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
を破棄しないことについて説明しています。 HttpRequestMessage
やHttpResponseMessage
はどうですか?
何か不足していますか?私は、消費するコードをHTTPに無知のままにしておきたいと思っていますが、これらすべての無秩序なオブジェクトを残すことは、習慣の年に反します!
したがって、呼び出しコードは、応答メッセージとストリームの所有権を把握し、所有権を取得する必要があるようです。または、応答メッセージを破棄せずにファイナライザーに処理させます。どちらのオプションも適切ではありません。
この特定の場合、-ファイナライザーはありません。 HttpResponseMessage
もHttpRequestMessage
もファイナライザーを実装していません(それは良いことです!)。どちらも廃棄しない場合は、GCが開始されるとガベージコレクションが行われ、それが発生すると、基になるストリームのハンドルが収集されます。
これらのオブジェクトを使用している限り、廃棄しないでください。完了したら、それらを破棄。それらをusing
ステートメントでラップする代わりに、完了したらいつでも明示的にDispose
を呼び出すことができます。どちらにしても、消費するコードはhttpリクエストの基礎となる知識を持っている必要はありません。
ストリームを入力パラメーターとして使用することもできます。そのため、呼び出し側は、ストリームの種類と処理方法を完全に制御できます。また、制御がメソッドを終了する前に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);
}
}
.NETでのDisposesの処理は簡単であり、かつ困難です。確かに。
ストリームはこれと同じナンセンスを引き出します...バッファを破棄すると、ラップされたストリームも自動的に破棄されますか?すべきですか?消費者として、それがあるかどうかを知る必要がありますか?
私がこのようなものを扱うとき、私はいくつかのルールに従っています:
したがって、HttpClient、HttpRequestMessage、およびHttpResponseMessageがあります。それらのそれぞれの寿命、およびそれらが作るあらゆる使い捨て品は尊重されなければなりません。したがって、yoはStreamをインスタンス化しないため、HttpResponseMessageのDispoableライフタイム以外では、Streamが存続することは期待できません。
あなたの上記のシナリオでは、私のパターンは、そのStreamを取得することが実際にStatic.DoGet(uri)メソッドにあるふりをすることであり、返されるStreamは私たち自身のものの1つになります。つまり、HttpResponseMessageのストリームが.CopyTo'dされた新しいストリーム(FileStream、MemoryStream、または状況に最も適したもの)を含む2番目のStream ...またはそれに類似したものを意味します。なぜなら:
したがって、キャッチアンドリリースなどの使い捨てを使用してください...それらを作成し、自分で結果をひっかかり、できるだけ早くそれらをリリースしてください。また、特に自分で作成しなかったクラスからの最適化を正確性と混同しないでください。