Silverlight上的ODATA仅在UI线程上运行

4
我们正在使用DataServiceCollection在Silverlight上与OData一起工作,以获取数据。
所有用于获取数据的调用(LoadAsync() LoadNextPartialSetAsync())都在工作线程上完成。但是,“LoadCompleted”回调以及反序列化和对象实例化都在UI线程上完成。
我们反编译了System.Data.Services.Client.DLL,其中包含DataServiceCollection,并发现所有处理OData响应的代码都被派遣到UI线程上。
是否有任何方法可以使反序列化在工作线程上调用?
谢谢 Yaron
3个回答

3

嗯……

看起来 OData 集合故意将处理移动到用户界面线程之外。我猜这是因为旧对象可能具有用户界面绑定的属性。在加载其他数据时,这些属性可能会发生更改。

使用查询本身,我能够在工作线程上获得响应。然而,这样做意味着必须从 OData 上下文中分离对象 (或者克隆它们),如果 UI 绑定了任何属性。否则,随后的查询可能会在对象在工作线程上创建时引起属性更改事件。


我很难理解你是如何在工作线程上实现物化的。在我的情况下,如果从除UI线程以外的任何其他线程调用LoadAsync(),就意味着失去浏览器的授权并返回401(我正在使用默认的XmlHttp堆栈,以便嵌入式SL应用程序使用现有的Web应用程序身份验证进行其服务调用)。 - Eduard Uta
1
老实说,这是将近6年前的事情了,我已经完全不记得当时具体做了什么了 :) - Yaron

1
你遇到的问题是 DataServiceCollection<T> 被继承自 ObservableCollection<T>。而后者被设计用于和 UI 元素进行绑定。当对 ObservableCollection<T> 的成员进行更改时,观察它的绑定表达式会收到通知。该绑定表达式随后试图更新目标 UI 元素。如果通知到达的线程不是 UI 线程,则会出现异常。

因此,DataServiceCollection<T> 有意将实例化转移到 UI 线程,以便当项目出现在集合中时,所产生的更改通知不会导致异常。如果这种行为对您不可接受,则应该放弃使用 DataServiceCollection<T>

相反,通过 DataServiceQuery<T>.BeginExecute 自己执行查询。您传递给 BeginExecute 的回调将在工作线程上执行(至少在使用 ClientHTTP 时是这样,我还没确定在使用 XmlHttp 时会发生什么)。在此处,您可以枚举结果并将其放置在您喜欢的任何集合类型中。准备好显示结果时,可以切换到 UI 线程。


更新:ODAta在上下文中保留它加载的对象。在我们的应用程序中,我们加载部分数据,将其移动到UI(以快速启动UI),然后加载剩余部分并更新UI。当实例化其他对象时,它们会连接到已经加载的对象,这些对象可以在OData上下文中找到,并且也绑定在UI中。这意味着无法在UI线程上执行实例化。在后台线程上以部分方式加载数据的唯一方法是创建与上下文分离的克隆。我想现在只能这样做了。 - Yaron

0
回调函数始终在UI线程上调用。如果请求正在使用XmlHttp堆栈(如果您从UI线程调用它,则为默认设置),则网络堆栈会在UI线程上调用由WCF Data Service在UI线程上注册的回调函数。因此,在这种情况下,它是DataServiceCollection/DataServiceContext的行为,但也是底层网络堆栈的行为。
如果您从非UI线程调用请求或显式设置Http堆栈为Client,则回调将返回到非UI线程(可能是另一个线程)。在通知调用者之前,我们仍然将其移回到UI线程。这样做的原因是一致性,特别是因为您无法在后台线程上与UI元素交互。
如果您手动执行查询,例如通过DataServiceContext.BeginExecute,则大部分实体化(或多数实体化)由调用方驱动,因为调用返回尚未填充的IEnumerable。然后,如果您将执行转移到工作线程并在那里枚举结果,则实体化将在该线程上发生。
只是好奇,为什么要移动它?您是否处理了大量数据,导致UI出现明显的滞后?

虽然我不太敢质疑那些在名字后面加上“MSFT”字样的人,但是:1)DataServiceCollection肯定会有意地将东西推到UI线程。2)我相信DataServiceContext.BeginExecute始终会在一个工作线程上执行回调,当在ClientHttp中选择HttpStack时(在疯狂的XmlHttp下它所做的事情是任何人的猜测)。当然我可能错了,但我不这么认为。 - AnthonyWJones
Vitek,你的评论完全不正确。WCF将根据请求线程处理网络回复的线程。如果您的请求在UI线程上完成,则它将在UI线程上处理回复;否则,它将不会。如果我没记错的话,可以通过某个属性来更改此行为。至于DataServiceCollection,它有显式代码将处理委托到UI线程。使用反射器与VC的集成,我实际上可以在那里设置断点并看到HTTP回复在工作线程上返回并委托给UI线程。 - Yaron
很抱歉,我之前的回答有些混淆和部分错误。如果您使用的是默认的XmlHttp并从UI线程调用请求,则是正确的。但如果您从非UI线程调用请求,则不适用于客户端堆栈。我已经更新了上面的回答以反映这一点。 - Vitek Karas MSFT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接