Quand ou si disposer du message HttpResponseMessage lors d'un appel à Readastreamasync?

j'utilise le System.Net.Http.HttpClient pour faire une communication HTTP côté client. J'ai tout le HTTP en un seul endroit, abstrait du reste du code. Dans un cas, je veux lire le contenu de la réponse comme un flux, mais le consommateur du flux est bien isolé de l'endroit où la communication HTTP se produit et le flux est ouvert. A l'endroit responsable de la communication HTTP je me débarrasse de tous les trucs HttpClient .

cet essai unitaire échouera à 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
}

j'essaie habituellement de disposer de tout ce qui est IDisposable le plus tôt possible, mais dans ce cas, la disposition du HttpResponseMessage dispose également du Stream retourné du ReadAsStreamAsync .

il semble donc que le code appelant doit connaître et prendre en charge le message de réponse ainsi que le flux, ou je laisse le message de réponse non contesté et laisse le finaliseur traiter avec elle. Aucune des deux options ne semble juste.

Cette réponse parle de ne pas disposer du HttpClient . Que pensez-vous du HttpRequestMessage et/ou HttpResponseMessage ?

est-ce que je manque quelque chose? J'espère garder le code de consommation ignorant de HTTP mais laisser tous ces objets non-divulgués autour va à l'encontre de l'année de l'habitude!

25
demandé sur Community 2014-12-31 05:17:30

3 réponses

donc il semble que le code d'appel doit savoir et prendre la propriété du message de réponse aussi bien que le flux, ou je laisse le message de réponse non contesté et laisser le finaliseur traiter avec elle. Aucune des deux options ne semble juste.

dans ce cas précis, il n'y a pas de finaliseurs . Ni HttpResponseMessage ni HttpRequestMessage n'implémentent un finalizer (et c'est une bonne chose!). Si vous ne mettez pas au rebut de de ceux-ci, les déchets seront ramassés une fois que le CG aura donné son coup de pouce, et la poignée de leurs cours d'eau sous-jacents sera ramassée une fois que cela se produira.

tant que vous utilisez ces objets, ne vous en débarrassez pas. Une fois fait, disposer d'eux . Au lieu de les envelopper dans une déclaration using , vous pouvez toujours explicitement appeler Dispose une fois que vous avez terminé. Dans tous les cas, le code de consommation n'a pas besoin d'avoir de connaissances sur les requêtes http sous-jacentes.

9
répondu Yuval Itzchakov 2016-06-01 13:38:23

vous pouvez également prendre stream comme paramètre d'entrée, de sorte que l'appelant a le contrôle complet sur le type de flux ainsi que son élimination. Et maintenant vous pouvez également disposer de httpResponse avant que control ne quitte la méthode.

Voici la méthode d'extension pour 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);                
        }
    }
5
répondu LP13 2018-03-01 19:20:27

traiter avec dispose dans .NET est à la fois facile, et difficile. Assurément.

Streams tire ce même non-sens... Est-ce que l'élimination du tampon permet également d'éliminer automatiquement le flux qu'il a enveloppé? Devrait-il? En tant que consommateur, devrais-je même savoir si c'est le cas?

quand je traite avec ce genre de choses, je vais par quelques règles:

  1. Que si je pense qu'il y a non-natif ressources à jouer (comme, d'une connexion réseau!), Je n'ai pas toujours laisser le GC "s'en occuper". L'épuisement des ressources est réel, et un bon code s'en occupe.
  2. si un jetable prend un jetable comme paramètre, il n'y a aucun mal à couvrir mes fesses et à s'assurer que mon code dispose de chaque objet qu'il fabrique. Si mon code ne l'a pas fait, je peux l'ignorer.
  3. appel GCs ~finaliser, mais rien ne garantit jamais que finaliser (c.-à-d. votre destructeur personnalisé) invoque disposer. Il n'y a pas de magie, contrairement à l'avis au-dessus, donc vous devez en être responsable.

ainsi, vous avez un HttpClient, un HttpRequestMessage, et un HttpResponseMessage. Les vies de chacun d'entre eux, et tout jetable qu'ils font, doivent être respectés. Par conséquent, on ne devrait jamais s'attendre à ce que votre flux survive en dehors de la durée de vie indisponible de HttpResponseMessage, parce que vous n'a pas instancié le flux.

Dans votre scénario ci-dessus, mon modèle ce serait prétendre que l'obtention de ce flux était vraiment juste dans un statique.La méthode DoGet(uri), et le flux que vous retournez devrait être un de notre propre fabrication. Cela signifie un second flux, avec le flux de la session HttpResponseMessage .CopyTo mon nouveau Flux (routage à travers un FileStream ou un MemoryStream ou quelle que soit la mieux adaptée à votre situation)... ou quelque chose de similaire. Parce que:

  • Vous n'avez pas le droit à la vie de HttpResponseMessage du Flux. C'est son, pas le vôtre. :)
  • maintenir la durée de vie d'un jetable comme HttpClient, pendant que vous broyez le contenu de ce flux retourné, est un bloqueur fou. Ce serait comme s'accrocher à une connexion SQL pendant que vous analysez un DataTable (imaginez à quelle vitesse nous mourrions de faim dans un pool de connexion si les DataTables devenaient énormes)
  • Exposer comment de recevoir cette réponse peut fonctionner contre SOLIDE... Vous aviez un ruisseau, qui est jetable, mais il venait d'un HttpResponseMessage, qui est jetable, mais cela ne s'est produit que parce que nous avons utilisé HttpClient et HttpRequestMessage, qui sont jetables... et tout ce que tu voulais, c'était un ruisseau d'URI. Comment la confusion n'ces responsabilités sentir?
  • Les réseaux
  • sont encore les voies les plus lentes des systèmes informatiques. Les retenir pour une" optimisation " est encore insensé. Il y a toujours de meilleures façons de manipuler les composants les plus lents.

ainsi utiliser des produits jetables comme la capture et la libération... faites-les, accrochez les résultats pour vous, libérez-les aussi vite que possible. Et ne confondez pas l'optimisation pour l'exactitude, surtout dans les classes dont vous n'êtes pas l'auteur.

1
répondu Craig Brunetti 2018-09-05 16:05:19