我正在将 WPF 应用程序移植到 WP7,在此过程中我必须重构所有涉及网络的代码。旧代码在后台线程中使用WebRequest对象的同步方法,但这些方法在WP7中不再存在。
结果令人困惑,让我觉得我做错了什么。我不得不用线程调度代码来乱扔我的视图 - 我看到的唯一替代方案是将调度程序提供给应用程序的较低层,这会破坏平台独立性并混淆与 UI 的边界。我已经失去了从循环中通过网络进行链式调用的能力,而是让回调调用自身。我失去了 try/catch 错误处理,而是到处都有 OnSuccess 和 OnError 回调。我现在总是无意中在回调调用的后台线程中运行代码。我深深地记得那些能够从方法返回值的日子。
我知道连续传递风格应该很棒,但我认为上述所有内容都使代码变得更加脆弱且可读性较差,并且使线程问题变得比需要的更加复杂。
如果这个问题含糊不清,我很抱歉,我只是想知道我是否在这里错过了一些大局。
I'm porting a WPF app to WP7, and in the process I've had to refactor all the code that touches the network. The old code used the synchronous methods of the WebRequest object in background threads, but these methods no longer exist in WP7.
The result has been bewildering, and makes me feel like I'm doing something wrong. I've had to litter my views with thread dispatching code - the only alternative to this that I see is to supply the dispatcher to the lower tiers of the app, which would break platform-independence and muddy the boundary with the UI. I've lost the ability to make chained calls over the network from loops, and instead have callbacks invoking themselves. I've lost try/catch error handling and instead have OnSuccess and OnError callbacks everywhere. I'm now always unintentionally running code in background threads that are invoked by callbacks. I fondly remember the days when I was able to return values from methods.
I know continuation-passsing-style is supposed to be great, but I think all of the above has made the code more brittle and less readable, and has made threading issues more complex than they need to be.
Apologies if this question is vague, I'd just like to know if I'm missing some big picture here.
发布评论
评论(2)
这是Silverlight的限制,它需要异步网络访问(WCF代理调用、WebClient、WebRequest等)。所有同步网络依赖方法调用已从框架中删除。
说得粗俗点:欢迎来到异步编程。你做错的唯一一件事就是没有首先使调用异步:)
我不是 100% 清楚 MS 从 Silverlight 中的 Web 相关对象中删除同步调用的确切原因,但我听到的解释总是集中在一两个原因的组合:
也就是说,Silverlight 中的 WCF 代理具有始终在调用线程上执行回调的行为。这通常是 UI 线程,这意味着您不必执行任何调度。我不知道 Silverlight 中的 WebClient/WebRequest 是否也有这种行为。
至于调度程序,您可以考虑使用 SynchronizationContext。 MS 模式和实践 Prism 指南中的 MVVM 参考实现就是这样做的 - 在存储库(实际上调用抽象外部服务的数据访问类)中,它们有一个 SynchronizationContext 成员,该成员被初始化为 System.Threading.SynchronizationContext.Current 。如果在 UI 线程上调用构造函数(应该是),则这是 UI 线程。然后,服务调用的所有结果都将使用 mySynchronizationContext.Post 进行处理。
This is a limitation of Silverlight, which requires asynchronous network access (WCF proxy calls, WebClient, WebRequest, etc.). All synchronous network-reliant method calls have been removed from the framework.
To be crass: welcome to asynchronous programming. The only thing you did wrong was not making the calls asynchronous in the first place :)
I'm not 100% clear on the exact reasons MS removed the sync calls from web-dependent objects in Silverlight, but the explanations I hear always center on one or two reasons in some combination:
That said - WCF proxies in Silverlight have the behavior that they always perform their callback on the calling thread. This is most often the UI thread, meaning you don't have to do any dispatching. I do not know if WebClient/WebRequest in Silverlight share this behavior.
As for the dispatcher, you could look into using a SynchronizationContext instead. The MVVM reference implementation in the MS Patterns and Practices Prism guidance does this - in the repository (data access class that actually makes calls out to an abstracted external service), they have a SynchronizationContext member that is initialized to System.Threading.SynchronizationContext.Current. This is the UI thread, if the constructor is called on the UI thread (it should be). All results from the service calls are then handled with mySynchronizationContext.Post.
像这样的问题似乎表现得像公共汽车。你很长时间都没有看到任何一个,然后两个几乎同时出现。请参阅今天早些时候提出的这个问题的更具体版本的答案。
我必须同意你的观点,连续传递是很棘手的。一项真正有用的技术是借用 C#
yield return
构造来创建能够在异步操作之间维护状态的机器。有关真正好的解释,请参阅 Jeremy 的博客相似度。就我个人而言,我更喜欢“少即是多”的方法,因此 AsyncOperationService 是一小块代码。您会注意到,它对于成功和失败都有一个回调,并且没有接口可以仅实现中等委托
Action>
(类型为AsyncOperation
) code> 使其更方便。针对此问题进行编码的基本步骤是: -
AsyncOperation
的方法,仅保留必须异步的最小部分。通常会进行一些WebRequest
或 WCF 调用,但请注意足以通过异步位,请参阅我的其他答案以获得一个很好的示例。yeild
这些 AsyncOperations 并将调用代码更改为“运行”结果可枚举。最终代码看起来与您可能更熟悉的同步代码非常相似。
至于在后台线程上意外运行的内容,最后一个答案包括这个有用的 AsyncOperation: -
您可以将其用作运行中的最终
yield
,以确保代码在completed
中执行code> 回调正在 UI 线程上执行。必要时“翻转”在 UI 线程上运行的明显同步代码也很有用。Questions like this seem to behave like buses. You don't see any for ages then two come along almost at the same time. See this answer to a more concrete version of this question asked earlier today.
I have to I agree with you, continuation passing is tricky. A really useful technique is to borrow the C#
yield return
construct to create a machine that is able to maintain state between asynchronous operations. For a really good explanation see this blog by Jeremy Likness.Personally I prefer a "less is more" approach so the AsyncOperationService is a very small chunk of code. You'll note that it has a single callback for both success and failure and there no interfaces to implement just a moderate delegate
Action<Action<Exception>>
which is typed asAsyncOperation
to make it more convenient.The basic steps to coding against this are:-
AsyncOperation
fpr only the smallest part that has to be asynchronous. Usually someWebRequest
or WCF call but note just enough to get past the async bit, see me other answer for a good example.yeild
these AsyncOperations and change the calling code to "Run" the resulting enumerable.The final code looks quite similar to the synchronous code you might be more familar with.
As to accidentally running things on a background thread, that last answer included this useful AsyncOperation:-
You can use that as the final
yield
in the run to ensure that code executing in thecompleted
callback is executing on the UI thread. Its also useful to "flip" what is apparently synchronous code to be running on the UI thread when necessary.