双工工作流服务示例
本示例演示如何在两个通信服务间执行异步双工通信。本示例还演示如何使用消息执行本地主机到工作流的通信。在两个服务之间执行双工通信时,这两个服务必须首先交换上下文,然后才能进行双向通信。启动通信的服务在其消息的答复中接收上下文。为支持从接收服务到启动服务的通信,启动服务必须在第一个消息中发送其上下文信息。
提示
此示例需要安装 .NET Framework 3.5 版才能生成和运行。若要打开项目和解决方案文件,需要使用 Visual Studio 2008。
有关 设置此示例的更多信息,请参见 One-time Setup Procedure for Windows Communication Foundation Samples。
本地主机和服务之间的通信也使用消息。本地主机公开可供服务用于回调的已知终结点。本示例实现一个 LocalWorkflowServiceHost
类型,该类型提供用于创建本地侦听器及所承载服务的工作流服务主机的功能。
本示例包括下面的通信实体:
客户端主机
客户端主机通过对IReverseContract
调用操作与客户端工作流通信。客户端主机首次调用BeginWork
操作时将会创建客户端工作流。在响应BeginWork
操作时,客户端工作流将发回一个包含客户端工作流上下文的消息。主机创建的用于与客户端工作流通信的通道此时将具有进行进一步通信的上下文。本示例演示客户端主机如何使用同一个通道向客户端工作流发送多个工作项。
若要使工作流能够与客户端主机进行通信,客户端主机应具有工作流所侦听的已知终结点。在本示例中,该已知终结点名为HostEndPoint
。LocalWorkflowServiceHost
用于创建客户端主机的已知终结点和客户端工作流的工作流服务主机。LocalWorkflowServiceHost localHost = new LocalWorkflowServiceHost(typeof(ClientWorkflow),new ClientHost()); localHost.WorkflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e) { Console.WriteLine("WorkflowTerminated: " + e.Exception.Message); }; localHost.WorkflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { Console.WriteLine("WorkflowCompleted: " + e.WorkflowInstance.InstanceId.ToString()); }; localHost.Open();
客户端工作流(作为服务公开)
该客户端工作流实现IReverseContract
。服务工作流使用此协定(如前面的图示所示)与客户端工作流通信。客户端工作流首次调用BeginWorkflow
操作时将会创建服务工作流。答复将返回服务工作流的上下文,在与服务工作流的后续客户端工作流通信中将使用此上下文。作为BeginWorkflow
操作的一部分,客户端工作流将其自己的终结点引用(包含终结点地址和上下文标头)传递给服务工作流。服务工作流使用此终结点地址与客户端工作流进行异步通信。
处于初始状态的WaitForBeginWork
事件处理程序实现名为DoSetReturnAddress
的代码活动。此活动设置发送给服务工作流的终结点引用,如下面的示例所示。private void SetReturnAddress(object sender, EventArgs e) { EndpointAddress epr = ContextManager.CreateEndpointAddress(ReturnUri, this.ReceiveWorkItemComplete); ReturnAddress = EndpointAddress10.FromEndpointAddress(epr); DebugOutput("[ClientWorkflow:SetReturnAddress] " + epr.Headers[0].GetValue<string>()); }
服务主机
本示例实现一个工作流服务主机。它打开一个侦听客户端请求的侦听器。来自客户端的第一个请求可创建服务工作流的实例。所有后续请求都将路由到同一个工作流实例,因为这些请求在消息头中包含上下文。服务工作流(作为服务公开)
服务工作流实现IForwardContract
。BeginWorkflow
操作中的服务工作流接收客户端工作流的终结点引用。服务工作流会将该终结点引用应用于以异步方式向客户端工作流发送消息的 Send 活动。WaitForBeginWorkflow
事件处理程序提供BeginWorkflow
Receive 活动。在 Receive 活动内部存在名为ApplyReturnAddress
的代码活动,此代码活动在 Send 活动中使用返回地址以便将消息发送给客户端工作流,如下面的代码所示。private void ApplyReturnAddress(object sender, EventArgs e) { // apply ReturnAddress to ReverseEndpoint EndpointAddress epr = ReturnAddress.ToEndpointAddress(); ContextManager.ApplyEndpointAddress( this.SendWorkItemComplete, epr); DebugOutput("[ServiceWorkflow:ApplyReturnAddress] " + epr.Headers[0].GetValue<string>()); }
LocalWorkflowServiceHost
类创建主机到工作流通信所必需的基础结构。本地工作流服务执行两项主要任务:- 创建本地侦听器以便主机可以侦听来自工作流的消息。
- 实例化 WorkflowServiceHost 以创建工作流的侦听器。
该类还为主机提供帮助器函数,以便保持用于与工作流通信的上下文。如果主机被回收,则此上下文用于与工作流实例通信。
WorkflowServiceUtility 项目提供操作上下文所需的所有帮助器函数。它提供的函数可用于从通道中提取上下文、对通道应用上下文和对发送活动应用终结点地址。
设置、生成和运行示例
若要安装永久性提供程序,请运行位于 One-Time Setup Procedure for the Windows Communication Foundation Samples主题中的 CreateStores.cmd 脚本。
下载并保存工作流服务实用工具,使 DuplexWorkflowService 和 WorkflowServiceUtility 文件夹位于同一个父文件夹中。
如果不想使用永久性提供程序,请在 App.config 文件中注释掉
<WorkflowRuntime>
节。本示例使用两个持久性数据库。服务工作流使用
NetFx35Samples_ServiceWorkflowStore
,客户端使用NetFx35Samples_ClientWorkflowStore
。您可以在 SQL Server 或 SQL Server Express 中创建这些存储。位于 App.config 文件中的当前连接字符串假定您要使用 SQL Server Express。如果您在 SQL Server 中创建这些存储,请务必更改 App.config 文件中的连接字符串。客户端主机和服务主机运行后,在控制台窗口中按 Y 可处理工作项。您可以发送多个工作项进行处理。如果您关闭并重新启动客户端,客户端将从停止处继续执行。
若要测试服务的持久性,请关闭客户端应用程序和服务应用程序,然后先重新启动客户端应用程序,再重新启动服务应用程序。这是为了保持先后顺序,因为只要服务工作流正在运行,它就会尝试将消息发回客户端;如果客户端不可用,则您会收到异常。
在客户端上,上下文存储在 Client.ctx 文件中。该文件存储在您的示例的 \bin 目录中。重新打开客户端时,此客户端将检查文件是否存在。如果文件存在,则客户端会将存储的上下文应用于要创建的通道。如果工作流服务已经完成并且您打开了此客户端,而 Client.ctx 文件仍在您的 \bin 目录中,则它将尝试对通道应用上下文。这会产生错误,因为不存在要与其进行通信的工作流实例。请删除文件并重试。