Silverlight的RIA应用中访问远端的WebService或WCF服务,都是通过异步线程模式调用的。在某些情况下我们的调用是需要同步进行,虽然Silverlight没有内置同步线程模式调用远端服务接口,但是我们可以通过多线程的处理来伪装出同步调用的实现。在.NET Framework的多线程编程中提供了丰富的线程接口,其中AutoResetEvent和
ManualResetEvent 在多线程编码中最为常用,本文将介绍如何通过AutoResetEvent的线程等待特性实现Silverlight同步调用远端WCF服务。
一、定义WCF服务
为了演示同步调用WCF服务的实现,提供一个简单的WCF服务接口,完成返回一本图书基本信息,WCF服务接口定义如下:
[ServiceContract]
public
interface
IDataService { [OperationContract] Book GetBook(); }
public
class
Book {
public
int
ID {
get
;
set
; }
public
string
Name {
get
;
set
; }
public
string
Author {
get
;
set
; }
public
double
Price {
get
;
set
; } }
接口提供一个返回图书基本信息的方法,包括图书编好,图书名,图书作者以及图书价格。接口具体的实现如下代码:
public
class
DataService : IDataService {
public
Book GetBook() {
return
new
Book { ID
=
1001
, Name
=
"
《三国演义》
"
, Author
=
"
罗贯中
"
, Price
=
89.50
}; } }
如上提供可正常运行的WCF服务接口,在需要调用接口的地方通过WEB引用既可生成该服务的客户端代理对象。
二、基于MVVM模式的视图模型
MVVM模式的核心为INotifyPropertyChanged接口,对于实体模型对象和UI控件元素间提供了完善的同步更新特性。为了方便界面元素同步更新,这里引入了MVVP模式的简单应用。
public
class
ViewModelBase : INotifyPropertyChanged {
public
event
PropertyChangedEventHandler PropertyChanged;
protected
void
RaisePropertyChangedEvent(
string
propertyName) { var handler
=
PropertyChanged;
if
(handler
!=
null
) handler(
this
,
new
PropertyChangedEventArgs(propertyName)); } }
还需要对应于服务接口中的Book对象定义一个ViewModel对象,详细如下代码所示:
public
class
BookViewModel : ViewModelBase {
private
int
iD;
///
<summary>
///
图书ID
///
</summary>
public
int
ID {
get
{
return
iD; }
set
{ iD
=
value; RaisePropertyChangedEvent(
"
ID
"
); } }
private
string
name;
///
<summary>
///
图书名称
///
</summary>
public
string
Name {
get
{
return
name; }
set
{ name
=
value; RaisePropertyChangedEvent(
"
Name
"
); } }
private
string
author;
///
<summary>
///
图书作者
///
</summary>
public
string
Author {
get
{
return
author; }
set
{ author
=
value; RaisePropertyChangedEvent(
"
Author
"
); } }
private
double
price;
///
<summary>
///
图书价格
///
</summary>
public
double
Price {
get
{
return
price; }
set
{ price
=
value; RaisePropertyChangedEvent(
"
Price
"
); } } }
三、基于AutoResetEvent的同步实现
利用AutoResetEvent的线程等待特性,可以折中实现Silverlight同步调用远端WCF服务。其原理就是在Silverlight发起异步调用远端WCF的时候进行线程阻塞,比记录异步调用远端WCF服务接口的完成事件,当异步调用完成后就终止线程阻塞,从而获取状态事件对象中或得调用远程接口所返回的结果。由于视图模型对象实现了INotifyPropertyChanged接口能够及时的更新界面元素,以此间接的就实现了同步方式调用。
public
class
AsyncCallStatus
<
T
>
{
public
AsyncCallStatus() { }
public
T CompletedEventArgs {
get
;
set
; } }
public
class
BookFacade {
private
AutoResetEvent autoResetEvent
=
new
AutoResetEvent(
false
);
public
void
GetBook(BookViewModel viewModel) {
if
(viewModel
==
null
) {
throw
new
ArgumentNullException(
"
viewModel
"
,
"
参数不能为空。
"
); } DataService.DataServiceClient client
=
new
DataService.DataServiceClient(); client.GetBookCompleted
+=
client_GetBookCompleted; var status
=
new
AsyncCallStatus
<
GetBookCompletedEventArgs
>
(); client.GetBookAsync(status);
//
阻塞线程
autoResetEvent.WaitOne();
if
(status.CompletedEventArgs.Error
!=
null
) {
throw
status.CompletedEventArgs.Error; } var book
=
status.CompletedEventArgs.Result; viewModel.ID
=
book.ID; viewModel.Name
=
book.Name; viewModel.Author
=
book.Author; viewModel.Price
=
book.Price; }
private
void
client_GetBookCompleted(
object
sender, GetBookCompletedEventArgs e) { var status
=
e.UserState
as
AsyncCallStatus
<
GetBookCompletedEventArgs
>
; status.CompletedEventArgs
=
e;
//
终止线程阻塞
autoResetEvent.Set(); } }
四、Silverlight前端调用
Siverlight前端就简单布局一个表单作为数据呈现界面,其代码如下:
<
Grid
x:Name
="LayoutRoot"
Background
="White"
>
<
Grid
HorizontalAlignment
="Left"
Name
="grid1"
VerticalAlignment
="Top"
Width
="300"
Margin
="20"
>
<
Grid.RowDefinitions
>
<
RowDefinition
Height
="30"
></
RowDefinition
>
<
RowDefinition
Height
="30"
></
RowDefinition
>
<
RowDefinition
Height
="30"
></
RowDefinition
>
<
RowDefinition
Height
="30"
></
RowDefinition
>
<
RowDefinition
Height
="30"
></
RowDefinition
>
</
Grid.RowDefinitions
>
<
Grid.ColumnDefinitions
>
<
ColumnDefinition
Width
="60"
></
ColumnDefinition
>
<
ColumnDefinition
Width
="*"
></
ColumnDefinition
>
</
Grid.ColumnDefinitions
>
<
sdk:Label
HorizontalAlignment
="Left"
Content
="图书编号:"
VerticalAlignment
="Center"
Grid.Column
="0"
Grid.Row
="0"
/>
<
TextBox
Text
="
{Binding ID}
"
Grid.Column
="1"
Grid.Row
="0"
></
TextBox
>
<
sdk:Label
HorizontalAlignment
="Left"
Content
="图书名称:"
VerticalAlignment
="Center"
Grid.Column
="0"
Grid.Row
="1"
/>
<
TextBox
Text
="
{Binding Name}
"
Grid.Column
="1"
Grid.Row
="1"
></
TextBox
>
<
sdk:Label
HorizontalAlignment
="Left"
Content
="图书作者:"
VerticalAlignment
="Center"
Grid.Column
="0"
Grid.Row
="2"
/>
<
TextBox
Text
="
{Binding Author}
"
Grid.Column
="1"
Grid.Row
="2"
></
TextBox
>
<
sdk:Label
HorizontalAlignment
="Left"
Content
="图书价格:"
VerticalAlignment
="Center"
Grid.Column
="0"
Grid.Row
="3"
/>
<
TextBox
Text
="
{Binding Price}
"
Grid.Column
="1"
Grid.Row
="3"
></
TextBox
>
<
Button
Content
="查询"
Grid.Column
="1"
Grid.Row
="4"
Width
="60"
Height
="23"
Click
="Button_Click"
></
Button
>
</
Grid
>
</
Grid
>
通过按钮执行调用WCF服务接口查询图书信息,按钮事件直接使用上面所写的图书门面类(BookFacade)的调用服务方法即可。
private
void
Button_Click(
object
sender, RoutedEventArgs e) {
try
{ ThreadPool.QueueUserWorkItem(
delegate
(
object
o) { BookViewModel viewModel
=
new
BookViewModel();
new
BookFacade().GetBook(viewModel); Deployment.Current.Dispatcher.BeginInvoke(()
=>
this
.DataContext
=
viewModel); }); }
catch
(Exception ex) { MessageBox.Show(ex.ToString()); } }
最终的运行如下图所示效果:
版权说明
本文属原创文章,欢迎转载且注明文章出处,其版权归作者和博客园共有。为了保存作者的创作热情,请在转载后的明显位置标记本文出处。
作 者:Beniao
文章出处:http://beniao.cnblogs.com/ 或 http://www.cnblogs.com/