Преобразование нетривиального кода в новый асинхронный шаблон .NET - как обрабатывать циклы выхода - PullRequest
4 голосов
/ 20 февраля 2012

Я пишу библиотеку для работы с хранилищем таблиц Azure.Основной шаблон заключается в том, что данный HTTP-запрос возвращает число результатов в потоке контента и указатель на следующий набор результатов в заголовках.Поскольку результаты считываются из потока, они выдаются.Я использую библиотеку System.Net.Http (ранее Microsoft.Net.Http), которая в последней версии удалила синхронную версию HttpClient.Send и другие синхронные методы.Новая версия использует Задачи.Я использовал Задачи раньше, но не для чего-то такого сложного, и мне трудно начинать.

Вызовы, которые были преобразованы в асинхронный шаблон: HttpClient.Send, response.Context.ContentReadSteam.Я очистил код, чтобы показать важные части.

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);
}

Ниже приведен пример того, что я успешно конвертировал.

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);
            }
        }
    });

У кого-нибудь есть какие-либо предложения о том, как преобразовать цикл / выход в новый шаблон?

Спасибо!Erick

Ответы [ 2 ]

4 голосов
/ 20 февраля 2012

Как вы обнаружили, async не работает лучше всего с yield прямо сейчас.Несмотря на то, что они выполняют аналогичные преобразования кода, цель совершенно иная.

Существует два решения: одно - предоставить буфер и использовать подход типа производитель / потребитель.System.Tasks.Dataflow.dll полезно для управления буферами в сложном коде.

Другое решение - написать «асинхронный перечислитель».Это концептуально ближе к тому, что должен делать ваш код, но это решение намного сложнее, чем решение производителя / потребителя.

Тип "асинхронный перечислитель" обсуждается немного в этом видео на Rx, и вы можете загрузить его из экспериментального пакета Rx (обратите внимание, что, хотя это делается командой Rx, он на самом деле не использует Rx).

1 голос
/ 20 февраля 2012

Я бы предложил преобразовать цикл \ yield в некоторую форму очереди вывода, например, как в этой статье , используя BlockingCollection<T>. Поэтому вызывающая сторона вашего метода предоставляет вам очередь для отправки результатов.

Очередь удобна, потому что она отделяет производителя от потребителей, но это всего лишь один из вариантов. В более общем случае, вызывающая сторона предоставляет вам обратный вызов для выполнения каждого полученного вами результата. Этот обратный вызов должен быть асинхронным, он может запустить другую задачу, например.

...