如何:使用通道模型访问双工服务
本主题介绍如何创建可以访问 Windows Communication Foundation (WCF) 双工服务的 Silverlight 2.0 版本 客户端。
本文假定您已通过完成在如何:构建双工服务中描述的过程构建了 WCF 双工服务。本文中简要介绍的过程说明了如何构建 Silverlight 2.0 客户端,该客户端向双工服务发送订单,并由服务回调两次,第一次是在服务开始处理订单时,第二次是在订单完成时。
构建 Silverlight 双工客户端涉及大量异步调用,在调用过程中定义回调方法。这些异步调用使用标准结构。在以下过程中,为清楚起见省略了一些回调。在过程结尾提供了包含所有必要方法的完整源代码。
创建 Silverlight 客户端应用程序
在 Visual Studio 2008 中,通过完成下列步骤在 DuplexService 的当前解决方案中创建新的 Silverlight 客户端项目:
- 在**“解决方案资源管理器”(位于右上角)中包含该服务的同一解决方案中,右击当前解决方案(不是项目),依次选择“添加”和“新建项目”**。
- 在**“添加新项目”对话框中,选择您的首选编程语言(C# 或 Visual Basic)的 Silverlight,然后选择“Silverlight 应用程序”**模板,将其命名为 DuplexClient。使用默认的位置。
- 单击**“确定”**。
在**“添加 Silverlight 应用程序”向导中,接受默认选择“将此 Silverlight 控件链接到现有网站”和其他默认选择,然后单击“确定”**。
构建双工客户端
在**“解决方案资源管理器”中右击 DuplexClient 项目,然后选择“添加引用…”。单击“添加引用”对话框中的 .NET 选项卡(如果它尚未选中)并定位到 System.ServiceModel.PollingDuplex.dll 程序集,选中它,然后单击“确定”**。使用相同的步骤添加对 System.ServiceModel.dll 和 System.Runtime.Serialization.dll 程序集的引用。
请注意,Silverlight 2.0 SDK 附带有两个名为 System.ServiceModel.PollingDuplex.dll 的程序集。其中一个用于 WCF 双工服务,另一个用于 Silverlight 双工客户端。正确按照前面的步骤操作以到达正确位置,这一点很重要,以便您能够为双工客户端引用正确的程序集。 将下列 using 语句添加至 Page.xaml.cs 的顶部。
using System.ServiceModel; using System.ServiceModel.Channels;
使用以下代码覆盖 Page.xaml.cs 上的
Page
方法。SynchronizationContext uiThread; public Page() { InitializeComponent(); // Grab a reference to the UI thread. uiThread = SynchronizationContext.Current; // Instantiate the binding and set the time-outs. PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding() { InactivityTimeout = TimeSpan.FromMinutes(1) }; // Instantiate and open channel factory from binding. IChannelFactory<IDuplexSessionChannel> factory = binding.BuildChannelFactory<IDuplexSessionChannel>(new BindingParameterCollection()); IAsyncResult factoryOpenResult = factory.BeginOpen(new AsyncCallback(OnOpenCompleteFactory), factory); if (factoryOpenResult.CompletedSynchronously) { CompleteOpenFactory(factoryOpenResult); } }
此代码实例化 PollingDuplexBindingElement,该元素用于在 Silverlight 客户端上创建可以与 WCF 双工服务通信的通道。配置了此绑定后,Silverlight 通道将定期在网络层上轮询服务,并检查服务要发送给客户端的任何新消息。
InactivityTimeout
属性确定在客户端和服务之间没有任何消息交换的情况下,在客户端关闭其会话之前可以经过的时间间隔(以毫秒为单位)。请注意,此上下文中的“消息交换”指的是由服务或客户端上的方法发送的真实消息,而不是网络层上的轮询。于是,该绑定元素可用来异步打开通道工厂。此异步操作在调用
CompleteOpenFactory
方法后结束。OnOpenCompleteFactory
是标准回调,因此省略。我们将
CompleteOpenFactory
方法定义如下。应将此代码粘贴到 DuplexClient 命名空间的Page
方法后。void CompleteOpenFactory(IAsyncResult result) { IChannelFactory<IDuplexSessionChannel> factory = (IChannelFactory<IDuplexSessionChannel>)result.AsyncState; factory.EndOpen(result); // Factory is now open. Create and open the channel from the channel factory. IDuplexSessionChannel channel = factory.CreateChannel(new EndpointAddress("https://localhost:19021/Service1.svc")); IAsyncResult channelOpenResult = channel.BeginOpen(new AsyncCallback(OnOpenCompleteChannel), channel); if (channelOpenResult.CompletedSynchronously) { CompleteOpenChannel(channelOpenResult); } }
此代码使用通道工厂创建 IDuplexSessionChannel 通道。请注意,所指定的终结点地址是以前构建的 WCF 双工服务地址。可以通过以下步骤获得该地址:右击 DuplexService 项目中的 Service1.svc 文件,选择**“在浏览器中查看”**,然后在浏览器窗口中观察地址。
必须将您的服务生成的正确地址粘贴到此处的代码中,以便客户端可以访问您的服务。 将异步打开通道,并且此异步操作在调用
CompleteOpenChannel
方法时结束。OnOpenCompleteChannel
是标准回调,因此省略。将
CompleteOpenChannel
方法定义如下。string order = "Widgets"; void CompleteOpenChannel(IAsyncResult result) { IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState; channel.EndOpen(result); // The channel is now open. Send a message. Message message = Message.CreateMessage(channel.GetProperty<MessageVersion>(), "Silverlight/IDuplexService/Order", order); IAsyncResult resultChannel = channel.BeginSend(message, new AsyncCallback(OnSend), channel); if (resultChannel.CompletedSynchronously) { CompleteOnSend(resultChannel); } // Also start the receive loop to listen for callbacks from the service. ReceiveLoop(channel); }
该方法首先做的事情是构建要发送给服务的 Message。请注意,在构建消息时必须使用 Soap11,因为双工服务只支持不含 WS-Addressing 的 SOAP 1.1 消息。而且,消息操作必须与在服务上定义的
IDuplexService
协定中的操作名称匹配。然后使用该通道来异步发送消息,并且异步操作在调用CompleteOnSend
方法时结束。OnSend
和CompleteOnSend
是标准回调并已省略。将消息发送给服务后,
ReceiveLoop
方法将启动接收循环,用于侦听由服务发回的消息。我们将
ReceiveLoop
和CompleteReceive
方法定义如下。OnReceiveComplete 是标准回调并已省略。高级模式是ReceiveLoop
方法引发一个事件,用于侦听来自服务的消息。收到消息后,将调用CompleteReceive
回调。CompleteReceive
回调处理消息,然后可以结束接收循环,或者通过再次调用ReceiveLoop
继续循环。void ReceiveLoop(IDuplexSessionChannel channel) { // Start listening for callbacks. IAsyncResult result = channel.BeginReceive(new AsyncCallback(OnReceiveComplete), channel); if (result.CompletedSynchronously) CompleteReceive(result); } void CompleteReceive(IAsyncResult result) { // A callback was received. IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState; try { Message receivedMessage = channel.EndReceive(result); if (receivedMessage == null) { // Server closed its output session, can close client channel, // or continue sending messages to start a new session. } else { // Show the service response in the UI. string text = receivedMessage.GetBody<string>(); uiThread.Post(WriteText, "Service says: " + text + Environment.NewLine); // Check whether the order is complete. if (text == order + " order complete") { // If the order is complete, close the client channel. IAsyncResult resultFactory = channel.BeginClose(new AsyncCallback(OnCloseChannel), channel); if (resultFactory.CompletedSynchronously) { CompleteCloseChannel(result); } } else { // If the order is not complete, continue listening. ReceiveLoop(channel); } } } catch (CommunicationObjectFaultedException) { // The channel inactivity time-out was reached. } }
注意对 EndReceive 的调用,此调用得到从服务接收的 Message。我们通过调用 GetBody 处理此消息并反序列化其正文。当我们收到表明订单已完成的消息时,退出接收循环并关闭通道。
此处省略了
WriteText
方法和 Page.xaml 文件。它们的功能是将结果打印到 UI 控件上。请查看下面的代码摘要以检查此代码及其位置。OnCloseChannel
和CompleteCloseChannel
是标准回调并也已省略。Silverlight 双工客户端现已完成。使用本主题结尾处的代码添加省略的代码和 Page.xaml 文件后,运行示例时应当显示下列输出。
Client says: Sent order of Widgets Service will call back with updates Service says: Processing Widgets order Service says: Widgets order complete
示例
下列代码示例汇总了完成上述过程后的 Page.xaml.cs 文件中应具备的内容。
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
using System.Windows.Controls;
namespace DuplexClient
{
public partial class Page : UserControl
{
SynchronizationContext uiThread;
public Page()
{
InitializeComponent();
// Grab a reference to the UI thread.
uiThread = SynchronizationContext.Current;
// Instantiate the binding and set the time-outs.
PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()
{
InactivityTimeout = TimeSpan.FromMinutes(1)
};
// Instantiate and open channel factory from binding.
IChannelFactory<IDuplexSessionChannel> factory =
binding.BuildChannelFactory<IDuplexSessionChannel>(new BindingParameterCollection());
IAsyncResult factoryOpenResult =
factory.BeginOpen(new AsyncCallback(OnOpenCompleteFactory), factory);
if (factoryOpenResult.CompletedSynchronously)
{
CompleteOpenFactory(factoryOpenResult);
}
}
void OnOpenCompleteFactory(IAsyncResult result)
{
if (result.CompletedSynchronously)
return;
else
CompleteOpenFactory(result);
}
void CompleteOpenFactory(IAsyncResult result)
{
IChannelFactory<IDuplexSessionChannel> factory =
(IChannelFactory<IDuplexSessionChannel>)result.AsyncState;
factory.EndOpen(result);
// The factory is now open. Create and open a channel from the channel factory.
IDuplexSessionChannel channel =
factory.CreateChannel(new EndpointAddress("https://localhost:19021/Service1.svc"));
IAsyncResult channelOpenResult =
channel.BeginOpen(new AsyncCallback(OnOpenCompleteChannel), channel);
if (channelOpenResult.CompletedSynchronously)
{
CompleteOpenChannel(channelOpenResult);
}
}
void OnOpenCompleteChannel(IAsyncResult result)
{
if (result.CompletedSynchronously)
return;
else
CompleteOpenChannel(result);
}
string order = "Widgets";
void CompleteOpenChannel(IAsyncResult result)
{
IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
channel.EndOpen(result);
// The channel is now open. Send a message.
Message message =
Message.CreateMessage(channel.GetProperty<MessageVersion>(),
"Silverlight/IDuplexService/Order", order);
IAsyncResult resultChannel =
channel.BeginSend(message, new AsyncCallback(OnSend), channel);
if (resultChannel.CompletedSynchronously)
{
CompleteOnSend(resultChannel);
}
// Also start the receive loop to listen for callbacks from the service.
ReceiveLoop(channel);
}
void OnSend(IAsyncResult result)
{
if (result.CompletedSynchronously)
return;
else
CompleteOnSend(result);
}
void CompleteOnSend(IAsyncResult result)
{
IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
channel.EndSend(result);
// The message is now sent. Notify the user.
uiThread.Post(WriteText, "Client says: Sent order of " + order + Environment.NewLine);
uiThread.Post(WriteText, "Service will call back with updates" + Environment.NewLine);
}
void ReceiveLoop(IDuplexSessionChannel channel)
{
// Start listening for callbacks.
IAsyncResult result = channel.BeginReceive(new AsyncCallback(OnReceiveComplete), channel);
if (result.CompletedSynchronously)
CompleteReceive(result);
}
void OnReceiveComplete(IAsyncResult result)
{
if (result.CompletedSynchronously)
return;
else
CompleteReceive(result);
}
void CompleteReceive(IAsyncResult result)
{
// A callback was received.
IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
try
{
Message receivedMessage = channel.EndReceive(result);
if (receivedMessage == null)
{
// Server closed its output session, can close client channel,
// or continue sending messages to start a new session.
}
else
{
// Show the service response in the UI.
string text = receivedMessage.GetBody<string>();
uiThread.Post(WriteText, "Service says: " + text + Environment.NewLine);
// Check whether the order is complete.
if (text == order + " order complete")
{
// If the order is complete, close the client channel.
IAsyncResult resultFactory =
channel.BeginClose(new AsyncCallback(OnCloseChannel), channel);
if (resultFactory.CompletedSynchronously)
{
CompleteCloseChannel(result);
}
}
else
{
// If the order is not complete, continue listening.
ReceiveLoop(channel);
}
}
}
catch (CommunicationObjectFaultedException)
{
// The channel inactivity time-out was reached.
}
}
void WriteText(object text)
{
reply.Text += (string)text;
}
void OnCloseChannel(IAsyncResult result)
{
if (result.CompletedSynchronously)
return;
else
CompleteCloseChannel(result);
}
void CompleteCloseChannel(IAsyncResult result)
{
IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
channel.EndClose(result);
// The client channel is now closed.
}
}
}
下面显示了完成上述过程后的 Page.xaml 文件中的代码。
<UserControl x:Class="DuplexClient.Page"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="reply" />
</Grid>
</UserControl>
版权所有 © 2008 Microsoft Corporation。保留所有权利。