Хороший асинхронный шаблон для последовательных запросов WebClient - PullRequest
5 голосов
/ 19 марта 2010

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

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

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

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

  1. Войти ()
  2. GetAlbums ()

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

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

Ответы [ 4 ]

7 голосов
/ 19 марта 2010

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

* ** 1003 тысяча два *http://www.leading -edge-dev.de /? Р = 501

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

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

Они предоставляют способ «упорядочить» события, действуя только при соблюдении определенных условий - например, допустим, у вас есть метод «AuthenticationResult Authenticate (string user, string pass)» * * 1014

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

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

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

3 голосов
/ 19 марта 2010

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

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

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

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

1 голос
/ 21 декабря 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();

1 голос
/ 20 марта 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 - это просто утилита, которая отправляет обратно в пользовательский интерфейс из фонового потока.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...