Хороший шаблон Async для последовательных запросов WebClient

Большая часть кода, который я написал в .NET для выполнения вызовов REST, была синхронной. Поскольку Silverlight на Windows Phone поддерживает только асинхронные вызовы WebClient и HttpWebRequest, мне было интересно, какой хороший асинхронный шаблон для класса, который предоставляет методы, выполняющие вызовы REST.

Например, у меня есть приложение, которому нужно сделать следующее.

  1. Войти и получить токен
  2. Используя токен из #1, получить список альбомов
  3. Используя токен из #1, получите список категорий
  4. и т.д

мой класс предоставляет несколько методов:

  1. Авторизоваться()
  2. ПолучитьАльбомы()
  3. ПолучитьКатегории()

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

Каков хороший способ сделать это в моем классе, который предоставляет эти методы?


person Omar Shahine    schedule 19.03.2010    source источник


Ответы (4)


Это действительно зависит от того, что вы хотите делать с этой информацией. Если, например, вы пытаетесь отобразить список альбомов/категорий и т. д., одним из способов моделирования этого может быть

  1. Имейте один или несколько классов, которые реализуют интерфейс INotifyPropertyChanged и используются в качестве источников данных для ваших представлений (для примера посмотрите файлы в папке Models в новом PhoneListApplication).
  2. Запустите асинхронную операцию, чтобы войти в систему и получить токен, пусть обратный вызов асинхронного метода сохранит токен для вас и вызовет функцию, которая запустит асинхронную операцию для получения списка альбомов и категорий.
  3. Обратный вызов для асинхронной операции для получения списка альбомов/категорий может обновлять ObservableList (путем добавления в него элементов). Я бы представил, что у вас есть по одному классу для альбомов и категорий, каждый из которых имеет наблюдаемый список. В любом случае, когда вы закончите добавление, просто вызовите NotifyPropertyChanged с именем свойства, которое вы изменили, и ваши данные должны появиться.

Существует очевидная проблема со случаями, когда вы хотите подождать и не продолжать, пока не получите что-то обратно по сети (например, если вы хотите сохранить страницу входа в систему, пока не узнаете, что вы успешно прошли аутентификацию). В этом случае вы можете просто изменить страницу в асинхронном обратном вызове.

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

person Aurojit Panda    schedule 19.03.2010

Вы можете взглянуть на расширения фреймворка Reactive (Rx):

http://www.leading-edge-dev.de/?p=501< /а>

http://themechanicalbride.blogspot.com/2009/07/introduction-rx-linq-to-events.html

[править: ооо - нашел хорошую ссылку:] http://rxwiki.wikidot.com/101samples

Они предоставляют способ «упорядочить» события, действуя только при выполнении определенных условий — например, скажем, у вас есть метод «AuthenticationResult Authenticate (строковый пользователь, строковый проход)».

Вы можете сделать что-то вроде:

var foo = Observable.FromAsyncPattern<string, string, AuthenticationResult>
    (client.BeginAuthenticate, client.EndAuthenticate);
var bar = foo("username","password");
var result = bar.First();

Эффективное превращение асинхронного метода в синхронный. Вы можете расширить это, включив «цепочку»:

var bar = foo("username", "password")
    .Then(authresult => DoSomethingWithResult(authresult));

Аккуратные вещи. :)

person JerKimball    schedule 19.03.2010

Мы написали наш сервисный слой на стороне клиента со всеми сигнатурами асинхронных функций, которые выглядят следующим образом:

public void MyFunction(
  ArtType arg, 
  Action<ReturnType> success, 
  Action<FailureType> failure);

Код службы выполняет асинхронный вызов веб-службы, и когда он возвращается, он вызывает обратный вызов успеха, если вызов был успешным, и обратный вызов отказа, если произошла ошибка/исключение. Тогда код вызова выглядит примерно так:

MyServiceInstance.MyFunction(
  blahVar,
  returnVal => UIInvoker.Invoke(() => 
    {
      //some success code here
    }),
  fault => UIInvoker.Invoke(() => 
    {
      //some fault handling code here
    }));

(UIInvoker — это просто утилита, которая отправляет обратно в пользовательский интерфейс из фонового потока.)

person Jason Jackson    schedule 20.03.2010

Я собрал что-то более плавное.

Restful-Silverlight — это библиотека, которую я создал для работы как с Silverlight, так и с WP7.

Я включил код ниже, чтобы показать, как вы можете использовать библиотеку для получения твитов из Twitter.

Пример использования Restful-Silverlight для получения твитов из Twitter:


//silverlight 4 usage
List<string> tweets = new List<string>();
var baseUri = "http://search.twitter.com/";

//new up asyncdelegation
var restFacilitator = new RestFacilitator();
var restService = new RestService(restFacilitator, baseUri);
var asyncDelegation = new AsyncDelegation(restFacilitator, restService, baseUri);

//tell async delegation to perform an HTTP/GET against a URI and return a dynamic type
asyncDelegation.Get<dynamic>(new { url = "search.json", q = "#haiku" })
    //when the HTTP/GET is performed, execute the following lambda against the result set.
    .WhenFinished(
    result => 
    {
        textBlockTweets.Text = "";
        //the json object returned by twitter contains a enumerable collection called results
        tweets = (result.results as IEnumerable).Select(s => s.text as string).ToList();
        foreach (string tweet in tweets)
        {
             textBlockTweets.Text += 
             HttpUtility.HtmlDecode(tweet) + 
             Environment.NewLine + 
             Environment.NewLine;
        }
    });

asyncDelegation.Go();

//wp7 usage
var baseUri = "http://search.twitter.com/";
var restFacilitator = new RestFacilitator();
var restService = new RestService(restFacilitator, baseUri);
var asyncDelegation = new AsyncDelegation(restFacilitator, restService, baseUri);

asyncDelegation.Get<Dictionary<string, object>>(new { url = "search.json", q = "#haiku" })
               .WhenFinished(
               result =>
               {
                   List<string> tweets = new List();
                   textBlockTweets.Text = "";
                   foreach (var tweetObject in result["results"].ToDictionaryArray())
                   {
                       textBlockTweets.Text +=
                           HttpUtility.HtmlDecode(tweetObject["text"].ToString()) + 
                           Environment.NewLine + 
                           Environment.NewLine;
                   }
               });

asyncDelegation.Go();

person Amir    schedule 21.12.2010
comment
Похоже, что приведенный выше URL-адрес ссылки теперь перенесен на github.com/amirrajan/Restful. - person Stonetip; 02.09.2012
comment
Биты silverlight были перемещены в устаревший тег. - person Amir; 07.10.2012