Workflows Published via WCF 3.5 Activities
Visual Studio 2008 and the .NET Framework 3.5 added the Send and Receive activities for messaging-style communication via WCF to WF (Figure 7).
The Receive activity is similar to the WebServiceInput activity for ASMX services, but it can receive messages from any kind of WCF endpoint. The Send activity corresponds to the WebServiceInvoke activity for calling WS-I BasicProfile services, but the Send activity supports both asynchronous and request-response messaging patterns.
The formal service contract
definitions for these activities are based on WCF contracts, and
therefore support all transports and polices supported by WCF. This
means they can handle synchronous and asynchronous message exchange
patterns even over WCF’s duplex channels.
You can quickly bind a workflow to an existing WCF contract, as shown in the following example where we bind the ICustomerService interface to the Send and Receive activities with the Choose Operation dialog (Figure 8):
Example 7.
[ServiceContract] public interface ICustomerService { [OperationContract] CustomerInfoResponse GetCustomerInfo( CustomerInfoRequest request); [OperationContract(IsOneWay=true)] void SubmitOrder(OrderDataMessage msg); } [Serializable] [DataContract(Namespace= "http://example.org/Service/Customer")] public class CustomerInfoRequest { ... } [Serializable] [DataContract(Namespace = "http://example.org/Service/Customer")] public class CustomerInfoResponse { ... } [Serializable] [DataContract(Namespace = "http://example.org/Service/Order")] public class OrderDataMessage { ... }
|
You can also define a new WCF service contract with the Choose
Operation dialog if you don’t already have one before you build the
workflow. Visual Studio will create the necessary metadata to expose the
workflow as a service.
With Workflow Services
hosting, you expose your workflow like any other WCF service. You can
host the service in a custom container or using WAS and allow access to
the workflow through a .svc file.
Note
Contracts created in the Choose Operation dialog do not explicitly show up in code. They are defined implicitly by setting the ServiceOperationInfo on the Receive activity. You can still refer to them in endpoint configurations in the service’s application configuration file.
The options for
routing incoming messages to the appropriate workflow instance with WCF
are more flexible than with ASMX services. In addition to a cookie-based
solution, the WF runtime in .NET 3.5 can also perform routing based on
special correlation tokens in SOAP headers.
The threading model
for WCF-based services has similar limitations for overlapping
request-response operations as with ASMX-based solutions. Again, these
limitations can be mitigated through the use of asynchronous message
exchanges.
Workflows Published via WCF 4.0 Activities
WF 4.0 changes the
programming model for workflows in that workflows, and consequently
Workflow Services, are exclusively developed either declaratively in
XAML or imperatively in a .NET language, such as C#. Workflows are
strongly typed with interfaces defined via InParameter, OutParameter and InOutParameter properties.
Workflow Services interfaces
are defined by the Send and Receive activities inside the workflow.
Unlike with WF 3.5, there are no dialogs to support binding these
activities to pre-defined service contracts. However, there is
sufficient functionality to support contract-first style development.
The WF 4.0 Send and Receive activities can reference contract type definitions for their message types (Figure 9).
For example, a pair of Send and Receive activities could implement the request-response operation GetCustomerData in the ICustomerService:
Example 8.
[ServiceContract(Namespace = "http://example.org/contracts/CustomerService")] interface ICustomerService { [OperationContract] GetCustomerResponse GetCustomerData(GetCustomerRequest msg); } [DataContract(Namespace= "http://example.org/operations/customer" )] public class GetCustomerResponse { [DataMember] public Customer CustomerData; ... } [DataContract(Namespace = "http://example.org/operations/customer")] public class GetCustomerRequest { [DataMember] public string customerId; ... }
|
Implementing this contract
with the Workflow Designer requires that you set some properties
manually to bind to the contract instead of relying on a dialog-driven
process. You need to first configure the Receive property of the ReceiveRequest activity for the GetCustomerRequest message.
Then you can configure the activity according to the message style in the contract. If the service expects to receive just the GetResponseRequest message without a wrapping XML element, you select the Message option in the Content Definition dialog (Figure 10).
The
inferred contract for the service interface ignores the operation name
and expects the raw message inside the SOAP body as shown here:
Example 9.
<s:Body> <GetCustomerRequest xmlns= "http://example.org/operations/customer"> <customerId>20</customerId> </GetCustomerRequest> </s:Body>
|
For fine-grained control
over the message shape, including SOAP headers and element protection,
messages can be defined as Message Contracts with the MessageContract attribute, just like message types outside of Workflow Services. Selecting the XmlSerializer option for message serialization allows for even more control over the message formatting (Figure 11).
The Receive
activity can also be configured for a SOAP message containing an element
to represent the operation name if the service naming should reflect an
RPC-style operation. Instead of selecting the Message option in the
Content Definition dialog, you can choose the Parameters option and
define a parameter of the message type (Figure 12).
The resulting service interface expects the additional GetCustomerData element:
Example 10.
<s:Body> <GetCustomerData xmlns="http://tempuri.org/"> <GetCustomerRequest xmlns:d4p1= "http://example.org/operations/customer" xmlns:i= "http://www.w3.org/2001/XMLSchema-instance"> <d4p1:customerId i:nil="true" /> </GetCustomerRequest> </GetCustomerData> </s:Body>
|
To qualify the operation name element with an XML namespace, you set the ServiceContractName property of the Receive activity, as shown in Figure 13.