天天看点

WF4工作流服务和双工通信

双工通信在很多时候都很有用,比如一个服务允许通知用户当前的进度情况。一般情况下,我们可以使用WCF来实现,你可以通过使用指定CallbackContract的ServiceContract属性的服务使用双工,如服务器端的代码如下:

客户端代码如下:

但是这种方式并不适用于WF4中,因为WF4中没有ServiceContract属性,不能指定CallbackContract。然而WF4支持双工通信。

代替正常WCF的双工服务,工作流服务使用一种架构被称作持久化双工,这是一种更分离的方式来实现双工通信,他的优点是通信的两端都是独立工作的,这也意味着一般的双工通信的缺点在这里不复存在。我们甚至可以在回调端使用一个完全不同于原始请求的绑定。不过作为客户端有一个缺点就是必须创建自己的ServiceHost并且做为一个完整的WCF服务,比正常的双工通信要复杂一些。下面我们看下如何实现。

<a href="http://images.cnblogs.com/cnblogs_com/carysun/WindowsLiveWriter/WF4_88F7/duplex1_2.png"></a>

第一步创建工作流服务,这个和其他的WCF工作流服务应用程序是一样的,持久化的双工需要一个上下文绑定,所以我们首先将绑定更改为wsHttpContextBinding如下:

下一步添加一个客户端的控制台应用程序用来调用工作流服务,添加服务引用及如下代码:

然后我们添加双工部分,首先我们要在客户端添加一个ServiceHost暴漏一个回调契约和回调服务定义,不像正常的双工服务定义契约在服务端,我们需要在客户端定义契约,代码如下:

下一步宿主这个服务,代码如下:

下一步是使工作流服务调用返回给客户端。 为此,我们需要做一些的事情。 首先,我们要让工作流服务知道在哪里可以回调。 这是通过将一个 CallbackContextMessageProperty 回调地址添加到请求标头。

这可以通过使用一个 OperationContextScope 来替换proxy.GetDate() 实现,代码如下:

然后我们更新工作流本身,首先我们先增加一个CorrelationHandle变量表示回调关联。如下图:

<a href="http://images.cnblogs.com/cnblogs_com/carysun/WindowsLiveWriter/WF4_88F7/duplex2_2.png"></a>

下一步在GetData Receive活动的CorrelationInitializer中初始化callbackHandle,如下图:

<a href="http://images.cnblogs.com/cnblogs_com/carysun/WindowsLiveWriter/WF4_88F7/duplex3_2.png"></a>

下一步我们需要一个Send活动来发送响应给客户端,设置其CorrelatesWith为之前初始化的callbackHandle,如下图:

<a href="http://images.cnblogs.com/cnblogs_com/carysun/WindowsLiveWriter/WF4_88F7/duplex4_2.png"></a>

最后我们设置Send活动属性来匹配客户端的回调服务,如下:

<a href="http://images.cnblogs.com/cnblogs_com/carysun/WindowsLiveWriter/WF4_88F7/duplex5_2.png"></a>

注意上面的设置中我们没有设置AddressUri和EndpointAddress,因为他可以通过请求上下文和回调关联句柄来提供,我们还设置了basicHttpBinding绑定,因为这个是客户端使用的。

最后我们需要增加一个传递回来的消息,如下:

<a href="http://images.cnblogs.com/cnblogs_com/carysun/WindowsLiveWriter/WF4_88F7/duplex6_2.png"></a>

都完成之后,我们可以运行服务端和客户端,可以得到服务端的回调在客户端上:

<a href="http://images.cnblogs.com/cnblogs_com/carysun/WindowsLiveWriter/WF4_88F7/duplex7_2.png"></a>

如果忘记设置回调CorrelationHandle会导致客户端得不到消息,会得到一个异常“Endpoint with Name='&lt;not specified&gt;' and ServiceContract '&lt;not specified&gt;' has a null or empty Uri property. A Uri for this Endpoint must be provided.”,这是由于Send活动找不到他的回调地址导致。

不能从客户端传递空的上下文,CallbackContextMessageProperty有几个构造函数重载,一个只有一个EndpointAddress参数,没有上下文参数。看起来好像是一个很好的选择我们可以不用指定任何的上下文信息。不幸的是当Send活动尝试寻找他的回调地址的时候会导致ArgumentNullException异常,异常信息为:"Value cannot be null. Parameter name: context".