我正在尝试重构一些中等复杂的现有.NET代码,以便在Silverlight中使用。核心问题是所有Silverlight Web服务调用必须是异步的,并且现有代码是以相当典型的同步方式实现的。
典型操作的现有调用堆栈可能是这样的:
页面 - > GetPersonMethod - > PersonBusinessObject - > PersonDataObject - > CallWebservice - > (泡沫响应备份堆栈)
我的想法是将所有方法拆分为单独的Request和Response方法,并保留应用程序的整体结构。
相同操作的新调用堆栈将如下所示:
页面 - > GetPersonRequest - > PersonBusinessRequest - > PersonDataRequest - > WebserviceRequest
Page< - GetPersonResponse< - PersonBusinessResponse< - PersonDataResponse< - WebserviceResponse
核心问题:
这是一个糟糕的主意,我是否真的只是从一个更加异步的角度重写它?
假设我采用这种方法,如何保留嵌套响应的调用堆栈?
TIA -
-Eric
答案 0 :(得分:2)
这是两个限制因素。我接近这个的方法是创建一个“异步包装”函数来为你包装它。需要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实现为内部使用异步请求/响应的同步方法:
请参阅http://msdn.microsoft.com/en-us/library/system.net.webrequest.endgetrequeststream.aspx