silverlight 上的 odata 仅在 UI 线程上运行
我们正在 Silverlight 上使用 OData,使用 DataServiceCollection 来获取数据。
所有获取数据的调用(LoadAsync() LoadNextPartialSetAsync())都是在工作线程上完成的。但是,“LoadCompleted”回调以及反序列化和对象具体化是在 UI 线程中完成的。
我们反编译了 DataServiceCollection 所在的 System.Data.Services.Client.DLL,发现所有处理 OData 响应的代码确实都被分派到 UI 线程。
有没有办法让反序列化在工作线程上调用?
谢谢 亚龙
We're working with OData on Silverlight, using DataServiceCollection to get the data.
All calls for fetching data (LoadAsync() LoadNextPartialSetAsync()) are done on a worker thread. However, the 'LoadCompleted' callback as well as deserialization and object materialization are done the UI thread.
We decompiled the System.Data.Services.Client.DLL where the DataServiceCollection is, and saw that indeed all code handling the response of the OData is dispatched to the UI thread.
Is there any way to get the deserialization to be called on a worker thread instead?
thanks
Yaron
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
嗯...
看来 OData 集合故意转移到 UI 线程的处理。我猜这样做是因为旧对象可能具有 UI 绑定的属性。加载附加数据时,这些属性可能会发生变化。
使用查询本身,我能够在工作线程上获得响应。然而,这样做意味着如果 UI 绑定到任何属性,则必须将对象与 OData 上下文分离(或克隆它们)。否则,当对象在工作线程上具体化时,后续查询可能会导致属性更改事件。
Well...
It seems the OData collections deliberately moves processing the UI thread. I'm guessing this is done because old objects might have properties the UI is bound to. These properties might change when loading additional data.
Using the query itself, I was able to get the response on a worker thread. However, doing this means one MUST detach the objects from the OData context (or clone them) if UI is bound to any property. Otherwise consequent queries might cause property changed events when objects are materializing on the worker thread.
您遇到的问题是
DataServiceCollection
派生自ObservableCollection
。反过来,它被设计为绑定到 UI 元素。当ObservableCollection
的成员资格发生更改时,会通知观察它的绑定表达式。然后,此绑定表达式尝试更新目标 UI 元素。如果通知到达非 UI 线程,则会发生异常。因此,
DataServiceCollection
有意将具体化转移到 UI 线程,以便当项目出现在集合中时,生成的更改通知不会导致异常。如果您无法接受此行为,则DataServiceCollection
不适合您。相反,您可以通过
DataServiceQuery.BeginExecute
自行执行查询。您传递给BeginExecute
的回调将在工作线程上执行(至少在使用 ClientHTTP 时会执行,我还没有确定使用 XmlHttp 时会发生什么)。您可以在此处枚举结果并将它们放入您喜欢的任何集合类型中。当您准备好显示结果时,可以切换到 UI 线程。The problem you have is that the
DataServiceCollection<T>
is derived from theObservableCollection<T>
. Which in turn is designed to be bound to UI elements. When changes are made to the membership of aObservableCollection<T>
a binding expression observing it is notified. This binding expression then attempt to update the target UI element. If the notification arrives on a non-UI Thread then an exception occurs.Hence the
DataServiceCollection<T>
deliberately shifts materialisation to the UI Thread so that as items appear in the collection the resulting change notifications do not result in an exception. If this behaviour is unacceptable to you thenDataServiceCollection<T>
is not for you.Instead execute the query yourself via the
DataServiceQuery<T>.BeginExecute
. The callback you pass toBeginExecute
will execute on a worker thread (at least it will when ClientHTTP is being used, I have yet established what will happen when XmlHttp is being used). Here you can enumerate the results and place them in whatever collection type your prefer. You can the switch to the UI thread when you are ready to display the results.回调将始终在 UI 线程上调用。
如果请求使用 XmlHttp 堆栈(如果从 UI 线程调用它,则这是默认堆栈),则网络堆栈将调用由 WCF 数据服务在 UI 线程上注册的回调。因此,在本例中,它是 DataServiceCollection/DataServiceContext 的行为,但也是底层网络堆栈的行为。
如果您从非 UI 线程调用请求,或者明确将 Http 堆栈设置为 Client,则回调将在非 UI 线程(可能是不同的线程)上返回。在让调用者知道之前,我们仍然将其移回 UI 线程。这样做的原因是一致性,特别是因为您无法与后台线程上的 UI 元素进行交互。
如果您手动执行查询,例如通过 DataServiceContext.BeginExecute,则具体化(或大部分)由调用方驱动,因为调用仅返回尚未填充的 IEnumerable。如果您随后将执行转移到工作线程并枚举那里的结果,则具体化将在该线程上发生。
只是好奇,为什么要移动它?您是否正在处理如此多的数据,以至于会导致明显的 UI 滞后?
The callback will be called always on a UI thread.
If the request was using the XmlHttp stack (which is the default if you call it from a UI thread) then the networking stack invokes the callback registered by the WCF Data Service on the UI thread. So in this case it's a behavior of the DataServiceCollection/DataServiceContext but a behavior of the underlying network stack.
If you call the request from a non-UI thread or you explicitely set the Http stack to be Client then the callback will come back on a non-UI thread (potentially a different one). We still move it back to the UI thread before letting the caller know. The reason for this is consistency, especially since you can't interact with UI elements on background threads.
If you would execute the query manually, so for example through DataServiceContext.BeginExecute, then the materialization (or most of it anyway) is driven by the caller, since the call returns just IEnumerable which is not yet populated. If you would then transfer execution to a worker thread and enumerate the results there, the materialization will happen on that thread.
Just curious, why do you want to move it? Are you processing so much data, that it causes a visible UI lag?