在.NET中实现嵌套的异步“调用堆栈”方案

时间:2009-02-03 17:51:12

标签: .net silverlight web-services architecture

我正在尝试重构一些中等复杂的现有.NET代码,以便在Silverlight中使用。核心问题是所有Silverlight Web服务调用必须是异步的,并且现有代码是以相当典型的同步方式实现的。

典型操作的现有调用堆栈可能是这样的:

页面 - > GetPersonMethod - > PersonBusinessObject - > PersonDataObject - > CallWebservice - > (泡沫响应备份堆栈)

我的想法是将所有方法拆分为单独的Request和Response方法,并保留应用程序的整体结构。

相同操作的新调用堆栈将如下所示:

页面 - > GetPersonRequest - > PersonBusinessRequest - > PersonDataRequest - > WebserviceRequest

Page< - GetPersonResponse< - PersonBusinessResponse< - PersonDataResponse< - WebserviceResponse

核心问题:

  1. 这是一个糟糕的主意,我是否真的只是从一个更加异步的角度重写它?

  2. 假设我采用这种方法,如何保留嵌套响应的调用堆栈?

  3. TIA -

    -Eric

2 个答案:

答案 0 :(得分:2)

  1. 您无法阻止主Silverlight线程,或者您的UI将挂起。这就是SL中所有网络操作都被强制异步的原因。
  2. 除了UI线程之外,您无法从任何线程触摸UI。
  3. 这是两个限制因素。我接近这个的方法是创建一个“异步包装”函数来为你包装它。需要3个函数(代表): 1.在新线程(“f”)上执行(确保不捕获函数中的任何UI对象!) 2.执行异常(“econt”) 3.执行完成(“续”)

    两个延续都将通过System.Windows.Deployment.Current.Dispatcher.BeginInvoke在UI线程上调度。

    有了这个,您只需要将您的Web服务调用更改为“通过异步同步”,就像mbeckish所说的那样(使用ManualResetEvent,来自同步线程的WaitOne,在回调上设置)。

    帮助程序的代码可能看起来像这样(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()
      );
    }
    

    <强>更新 要进行sync-via-async调用,你可以这样做,假设你使用默认的“基于事件的异步模式”(使用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
    

答案 1 :(得分:0)

为避免重构,您可以将CallWebService实现为内部使用异步请求/响应的同步方法:

  1. 发出异步请求后,在ManualResetEvent上执行WaitOne()。
  2. 在回调中,在ManualResetEvent上执行Set()。
  3. 请参阅http://msdn.microsoft.com/en-us/library/system.net.webrequest.endgetrequeststream.aspx