Реализация вложенного асинхронного сценария «стек вызовов» в .NET - PullRequest
1 голос
/ 03 февраля 2009

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

Существующий стек вызовов для типичной операции может выглядеть примерно так:

Страница -> GetPersonMethod -> PersonBusinessObject -> PersonDataObject -> CallWebservice -> (пузырьковый ответ в стеке)

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

Новый стек вызовов для той же операции будет выглядеть так:

Страница -> GetPersonRequest -> PersonBusinessRequest -> PersonDataRequest -> WebserviceRequest

Страница <- GetPersonResponse <- PersonBusinessResponse <- PersonDataResponse <- WebserviceResponse </p>

Основные вопросы:

  1. Это ужасная идея, и я действительно должен просто переписать ее с нуля с более асинхронной перспективой?

  2. Если я выберу такой подход, как сохранить стек вызовов для моих вложенных ответов?

1027 * TIA-*

-Эрик

Ответы [ 2 ]

2 голосов
/ 04 февраля 2009
  1. Вы не можете заблокировать основной поток Silverlight, иначе ваш пользовательский интерфейс зависнет. Это причина того, что все сетевые операции в SL принудительно асинхронны.
  2. Вы не можете касаться пользовательского интерфейса из любого потока, кроме потока пользовательского интерфейса.

Это два ограничения. Я бы подошел к этому, чтобы создать функцию «асинхронной обертки», чтобы обернуть ее для вас. Это займет 3 функции (делегаты): 1. Выполнить в новом потоке ("f") (убедитесь, что вы не захватываете объекты пользовательского интерфейса в своей функции!) 2. Выполнить в порядке исключения («econt») 3. Выполнить по полной («продолжение»)

Оба продолжения будут отправлены в потоке пользовательского интерфейса через System.Windows.Deployment.Current.Dispatcher.BeginInvoke.

При этом вам просто нужно изменить вызовы веб-службы на «синхронизацию через асинхронный», как говорит mbeckish (используйте ManualResetEvent, WaitOne из потока синхронизации, Set on callback).

Код для помощника может выглядеть примерно так (psuedocode, не проверял):

static void AsyncHelp<T>(Func<T> f, Action<Exception> econt, Action<T> cont) {
  var t = new Thread((_) => {
    try {
      var res = f();
      System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => cont(res));
    } catch (Exception ex) {
      System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => econt(ex));
    }
  });
  t.Start();
}

Вы бы в конечном итоге использовали это так:

some_handler() {
  var id = mytextbox.Text; // Save from UI to local
  AsyncHelp( 
    () => GetBla(id), 
    bla => result.Text = bla.ToString(), // This is safe cause it's dispatched
    ex => error.Text = ex.ToString()
  );
}

Обновление Чтобы выполнить синхронизацию через асинхронный вызов, вы должны сделать что-то вроде этого, предполагая, что вы используете «Асинхронный шаблон на основе событий» по умолчанию (использовать BeginXXX / EndXXX проще, IMO).

Exception ex;
Result r;
var mre = new ManualResetEvent(false);
myService.OnGetBlaCompleted += (_, e) => {
  ex = e.Error;
  r = e.Result;
  mre.Set();
}
myService.GetBlaAsync(id);
mre.WaitOne();
if (ex != null) throw ex;
// and so on
0 голосов
/ 03 февраля 2009

Чтобы избежать рефакторинга, вы можете реализовать CallWebService как синхронный метод, который внутренне использует асинхронный запрос / ответ:

  1. После выполнения асинхронного запроса выполните WaitOne () для ManualResetEvent.
  2. В обратном вызове выполните Set () для ManualResetEvent.

См. http://msdn.microsoft.com/en-us/library/system.net.webrequest.endgetrequeststream.aspx

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