Convert non-trivial code to a new .NET async template - how to handle output cycles

I am writing a library to work with Azure Table Storage. The basic pattern is that the specified HTTP request returns the number of results in the content stream and a pointer to the next set of results in the headers. When the results are read from the stream, they are obtained. I use the System.Net.Http library (formerly Microsoft.Net.Http), which in the latest version removed the synchronous version of HttpClient.Send and other synchronous methods. The new version uses tasks. I used to use Tasks, but not for something so complicated, and it's not easy for me to start.

Calls that have been converted to an async template: HttpClient.Send, response.Context.ContentReadSteam. I cleared the code to show the important parts.

var queryUri = _GetTableQueryUri(tableServiceUri, tableName, query, null, null, timeout); while(true) { var continuationParitionKey = ""; var continuationRowKey = ""; using (var request = GetRequest(queryUri, null, action.Method, azureAccountName, azureAccountKey)) { using (var client = new HttpClient()) { using (var response = client.Send(request, HttpCompletionOption.ResponseHeadersRead)) { continuationParitionKey = // stuff from headers continuationRowKey = // stuff from headers using (var reader = XmlReader.Create(response.Content.ContentReadStream)) { while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element && reader.Name == "entry" && reader.NamespaceURI == "http://www.w3.org/2005/Atom") { yield return XElement.ReadFrom(reader) as XElement; } } reader.Close(); } } } } if (continuationParitionKey == null && continuationRowKey == null) break; queryUri = _GetTableQueryUri(tableServiceUri, tableName, query, continuationParitionKey, continuationRowKey, timeout); } 

Below is an example of the one I have successfully converted.

 client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ContinueWith(task => { using (var response = task.Result) { if (response.StatusCode == HttpStatusCode.Created && action == HttpMethod.Post) { return XElement.Load(response.Content.ReadAsStreamAsync().Result); } } }); 

Does anyone have any suggestions on how to convert a cycle / income to a new template?

Thanks! Errick

+4
source share
2 answers

As you have discovered, async is not working the best with yield right now. Although they perform similar code conversions, the goal is completely different.

There are two solutions: one is to provide a buffer and use the producer / consumer approach. System.Tasks.Dataflow.dll is useful for managing buffers in complex code.

Another solution is to write an "asynchronous enumerator." This is conceptually closer to what your code should do, but this solution is much more complex than the manufacturer / consumer solution.

The "async enumerator" type is discussed in this video on Rx , and you can download it from the experimental Rx package (note that even if it is executed by the Rx command, it does not actually use Rx).

+4
source

I would suggest converting loop \ yield into some form of output queue, for example, as in this article , using BlockingCollection<T> . Thus, the caller of your method provides you with a queue for outputting the results.

The queue is convenient because it separates the producer from the consumers, but this is just one option. In general, the caller provides you with a callback for each result you receive. This callback must be asynchronous, for example, it can run another task.

+1
source

Source: https://habr.com/ru/post/1397372/


All Articles