Téléchargement asynchrone d’un blob Azure vers une chaîne avec .NET 4.5 async, attend

J’essaie d’implémenter un téléchargement de blob totalement asynchrone avec .NET 4.5 async & wait.

Supposons que l’intégralité du blob puisse tenir en mémoire en une fois et que nous souhaitons le tenir dans une ssortingng .

Code:

 public async Task DownloadTextAsync(ICloudBlob blob) { using (Stream memoryStream = new MemoryStream()) { IAsyncResult asyncResult = blob.BeginDownloadToStream(memoryStream, null, null); await Task.Factory.FromAsync(asyncResult, (r) => { blob.EndDownloadToStream(r); }); memoryStream.Position = 0; using (StreamReader streamReader = new StreamReader(memoryStream)) { // is this good enough? return streamReader.ReadToEnd(); // or do we need this? return await streamReader.ReadToEndAsync(); } } } 

Usage:

 CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageAccountConnectionSsortingng")); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference("container1"); CloudBlockBlob blockBlob = container.GetBlockBlobReference("blob1.txt"); ssortingng text = await DownloadTextAsync(blockBlob); 

Ce code est-il correct et est-il totalement asynchrone? Souhaitez-vous mettre cela en œuvre différemment?

J’apprécierais quelques précisions supplémentaires:

  1. GetContainerReference et GetBlockBlobReference n’ont pas besoin d’être asynchrones, car ils ne contactent pas encore le serveur, n’est-ce pas?

  2. streamReader.ReadToEnd doit-il être asynchrone ou non?

  3. Je suis un peu confus quant à ce BeginDownloadToStream fait BeginDownloadToStream . Au moment d’ EndDownloadToStream , mon stream de mémoire contient-il toutes les données? ou le stream est-il seulement ouvert en pré-lecture?

Mise à jour: (à partir de Storage 2.1.0.0 RC)

Async maintenant supporté nativement.

 CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageAccountConnectionSsortingng")); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference("container1"); CloudBlockBlob blockBlob = container.GetBlockBlobReference("blob1.txt"); ssortingng text = await blockBlob.DownloadTextAsync(); 

Ce code est-il correct et est-il totalement asynchrone?

Oui.

Souhaitez-vous mettre cela en œuvre différemment?

Oui. En particulier, les wrappers TaskFactory.FromAsync sont beaucoup plus efficaces si vous transmettez une paire de méthodes Begin / End au lieu de transmettre un IAsyncResult existant. Comme ça:

 await Task.Factory.FromAsync(blob.BeginDownloadToStream, blob.EndDownloadToStream, memoryStream, null); 

Je préfère également les intégrer dans des méthodes d’extension distinctes pour pouvoir l’appeler ainsi:

 await blog.DownloadToStreamAsync(memoryStream); 

Notez que la prochaine version des bibliothèques clientes (2.1, actuellement dans RC) aura des méthodes async , à savoir DownloadToStreamAsync .

GetContainerReference et GetBlockBlobReference n’ont pas besoin d’être asynchrones, car ils ne contactent pas encore le serveur, n’est-ce pas?

Correct.

StreamReader.ReadToEnd doit-il être asynchrone ou non?

Ce n’est pas (et ne devrait pas). Stream est un cas peu commun avec la programmation async . Généralement, s’il existe une méthode async , vous devez l’utiliser dans votre code async , mais cette directive ne s’applique pas aux types de Stream . La raison en est que la classe Stream base ne sait pas si son implémentation est synchrone ou asynchrone. Elle suppose donc qu’elle est synchrone et simule par défaut ses opérations asynchrones en effectuant simplement le travail synchrone sur un thread en arrière-plan. Les stream véritablement asynchrones (par exemple, NetworkStream ) remplacent cette fonction et fournissent de véritables opérations asynchrones. Les stream synchrones (par exemple, MemoryStream ) conservent ce comportement par défaut.

Donc, vous ne voulez pas appeler ReadToEndAsync sur un MemoryStream .

Je suis un peu confus quant à ce que fait BeginDownloadToStream. Au moment d’appeler EndDownloadToStream, mon stream de mémoire contient-il toutes les données?

Oui. L’opération est DownloadToStream ; que cela, il télécharge un blob dans un stream. Étant donné que vous téléchargez un blob dans un MemoryStream , le blob est entièrement en mémoire au moment où cette opération est terminée.

  1. Correct, ils n’ont pas besoin d’être asynchrone s’ils ne vont pas faire de longues opérations, ce qu’ils ne devraient pas être.

  2. Probablement pas, bien que je ne connaisse pas cette implémentation particulière. J’espère que puisque vous attendez que le stream se termine avant ce point, il ne devrait pas y avoir de travail sur le réseau, et donc pas d’opérations coûteuses, à effectuer à ce stade. Vous devriez simplement extraire des données d’un tampon, et ce devrait être rapide. C’est assez facile à tester, cependant. Vous pouvez utiliser quelque chose comme Fiddler pour voir si une communication réseau est en cours pendant cet appel, vous pouvez simplement chronométrer la méthode pour voir si cela prend suffisamment de temps pour apparaître que des E / S réseau sont en cours, ou vous pouvez consulter la documentation correspondante. implémentation de stream spécifique. Ou vous pouvez simplement utiliser la méthode asynchrone pour être sûr, ce que je suggérerais, plutôt que de risquer de vous tromper. Je serais plutôt surpris de constater que cela devait être asynchrone.

  3. Voir n ° 2.

Voir: http://channel9.msdn.com/Events/TechEd/NorthAmerica/2013/WAD-B406#fbid=lCN9J5QiTDF pour connaître certaines des meilleures pratiques utiles, notamment pourquoi vous devez éviter d’utiliser le stream de mémoire comme le code d’origine 🙂

Une remarque est que vous avez deux options principales pour télécharger les méthodes Blobs, les méthodes Cloud [Block | Page] Blob.Download [Range] To * et le stream fourni par OpenRead (). Dans le cas de l’appel de téléchargement, l’ensemble du blob (ou de la plage si nécessaire) est émis sous la forme d’un seul appel GET et les résultats sont transférés / écrits à l’emplacement approprié. demandé conformément à la politique de nouvelle tentative.

Les méthodes OpenRead sont destinées aux clients qui souhaitent traiter des données sur une longue période sans conserver une connexion ouverte. Ils travaillent en spécifiant une longueur donnée qui sera pré-tamponnée du côté client. Lorsque le stream manque de données pré-tamponnées, la sous-plage suivante est demandée.

Enfin, à partir de la version 2.1 RTM, une méthode DownloadTextAsync est fournie et effectue tout cela à votre place 🙂 (avec des surcharges facultatives pour spécifier le codage, la valeur par défaut est UTF8)