WF Guidance Workflow Services Status Final Draft NET 4 Beta 2

Document Sample
WF Guidance Workflow Services Status Final Draft NET 4 Beta 2 Powered By Docstoc
					                                          WF Guidance: Workflow Services
                                                                                    Status: Final Draft, .NET 4 Beta 2
                                                                              Last Modified: 1/16/2012 10:04:12 AM




1 Introduction and scope
This paper presents example-oriented step-by-step instructions for redesigning workflows that implement
Windows Communication Foundation (WCF) web services (commonly referred to as workflow services)
created in the current implementation of Windows Workflow Foundation (consisting of the types in the
System.Workflow.* namespaces; referred to in this document as WF3) to workflows that use the new WF
implementation that is being introduced in .NET 4 (the types in the System.Activities.* namespaces;
referred in this paper as WF4). This paper covers common scenarios for out-of-box activities.

This document does not cover persistence or durable WCF services. We also cover the context-based
message correlation mechanism, but not any of the other mechanisms available in WF4.

The examples and instructions shown in this document use the Visual Studio workflow designer and the C#
language.

Please see the “WF Migration Overview” document for an introduction to WF3 to WF4 migration, and links
to related WF3 -> WF4 documents. The latest version of these documents can be found at
http://go.microsoft.com/fwlink/?LinkID=153313


2 WF3 WorkflowServices scenarios implemented in WF4
2.1 Simple messaging patterns
Our first example focuses on a simple messaging scenario where one workflow is acting strictly as a service
and another workflow is acting strictly as a client. We take the SequentialWorkflowService sample from
the .Net Framework 3.5 SP1 SDK as a starting point. The sample implements a calculator service, which
exposes six operations: PowerOn, PowerOff, Add, Subtract, Multiply, and Divide. Each operation is
represented by WF3 Receive activity configured with a request/reply messaging pattern (that means the
One Way Operation option in the activity options dialog is left unchecked). This pattern is also known as a
two-way pattern where, the service will send a reply for every message sent by the client.

As the workflow executes, the first operations that it listens for is the PowerOn Receive which is marked
with CanCreateInstance set to true so it can instantiate the workflow (this is also known as an activating
Receive). After PowerOn is received the workflow starts looping and listening for any of the other
operations (made possible by a Listen activity with nested EventDrivenActivity instances). Each
operation uses a CodeActivity inside the Receive to perform the appropriate mathematical operation



WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                              Page 1
and return the result, with the exception of the PowerOff operation, which causes the loop to end and the
workflow to terminate.




On the client side we arrange a series of Send activities (again configured for request/reply messaging)
that will exercise the operations of our calculator. We need to specify how each Send activity will
communicate with the service, so we use the ChannelToken property to point to an endpoint name
specified in the application .config file. Defining the endpoint in the .config file is a manual process that
requires knowledge of how the service is configured.

 Each Send activity uses its AfterResponse and BeforeSend event handlers to reference methods in code-
behind, which generate new random numbers to send to the service for the calculation, and output the
result of the calculation to the console.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                              Page 2
We proceed to migrate this scenario to WF4; the code for the two versions is available in the
SequentialWorkflowService project which accompanies this paper.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                            Page 3
The first thing to note is that every WF3 Receive activity has been replaced by a pair of WF4 activities: a
Receive activity and a SendReply activity. The work done by the Receive activity is no longer a child of
the activity, but a separate activity that comes in the sequence between Receive and SendReply.

In addition, the Listen activity with nested EventDriven activities in WF3, which allowed us to listen to
multiple events in parallel has now been replaced by a Pick activity. Lastly, in this implementation, we
replace the actual calculation work, previously accomplished in code via the CodeActivity, with a WF4
Assign activity which lets us perform the same operations without dropping into code.

When designing the client Workflow we no longer need to manually configure the activities to send
messages to the service. Using the Add Service Reference feature we can point to the running workflow
service and generate a set of pre-configured custom activities (such as PowerOn, Add, etc) that we can
drop inside our workflow, instead of having to manually configure Send activities. Furthermore, we use a
CorrelationScope activity to manage the context correlation; the scope is responsible for taking the
incoming context token from the first request (PowerOn) and re-attaching it to each subsequent request
(Add, Multiply, etc.). We have also created a custom activity called GenerateOutputValue, which
generates a new random number to send, which was done in code in WF3 using the BeforeSend event
handler. A WriteLine activity is used to display the result of the calculation instead of the AfterResponse
event handler.



WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                           Page 4
2.2 Complex messaging patterns
Our first example focused on a simple messaging scenario where one workflow is acting strictly as a service
and another workflow is acting strictly as a client. In this second example both “client” and “server”
workflows can send messages to each other, which blurs the notion of client and server. This pattern is
called duplex messaging. We start with the Conversations sample from the Net Framework 3.5 SP1 SDK.
The sample implements a customer-supplier relationship where the customer asks for a quote for a given
shipping order and the supplier calls out to three shipping services, collects their quotes for the order, and
returns the result to the customer. The duplex aspect of the communication pattern is evident in two
places: first, the supplier calls the customer after it takes a potentially arbitrary amount of time to collect all
the shipping quotes from the suppliers; second, each shipper calls back the supplier after spending a
potentially arbitrary amount of time pricing the order. These arbitrary delays can be thought of as the
human role in the workflow; it is possible that a person needs to evaluate the shipping quotes to find the
best one or person may need to assess the shipping order to price it correctly.

In the WF3 implementation below we have the workflow implementing the sevice. It starts with an
activating request/reply Receive called ReceiveSubmitOrder with an operation name SubmitOrder
which is called by the customer and takes two parameters: an object of type PurchaseOrder which
contains the details of the customer’s order, and a “context” object of type Dictionary<string,
string>. This second “context” object is required to implement the duplex pattern in WF3. In order for the
service to be able to call back the client, the client uses an operation parameter in the first Receive to send
a context token for the service to attach to the request back to the client. This will be discussed in more
detail further on in this paper. A CodeActivity is used to store the PurchaseOrder and context token as
workflow variables and print out the PurchaseOrder to the command line.

The next element in the workflow is a Parallel activity which obtains the best quote by communicating
with three shippers. We will omit the details of this process and address the reason for that further on in
this paper. The last activity in the workflow is a request/reply Send activity called SendOrderDetails
with an operation name OrderDetails. This sends the best shipping quote back to the customer. As we
mention above, the service needs to send the customer a context token, which is accomplished by setting
SendOrderDetails.Context to the context token provided to the SubmitOrder operation.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                               Page 5
The customer workflow has a request/reply Send called SendSubmitOrder as its first activity to activate
the supplier workflow. The Send uses a code-behind and its BeforeRequest property to instantiate a
PurchaseOrder to send to the supplier and also to send the workflow’s context to the supplier so it can
call back the customer.



WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                       Page 6
Below we see the same workflow migrated to WF4. The code can be found in the Conversations solution
which accompanies this paper.

In this paper, we do not port the full sample. The portion of the sample where a Parallel activity is used to
call out and gather quotes from multiple shippers has not been migrated. Instead a hardcoded
ShippingQuote is used to simulate the best shipping quote the service has. The reason for this
simplification is that implementing the parallel calls requires the use of the conversations messaging
patttern, which is supported in WF4 by using more generic content-based correlations (which are out of
the scope of this paper). The correlation model in WF4 is considerably more powerful than in WF3, which
allows correlations to be created based on any piece of data in a message, not just a particular conversation
id. Content-based correlations are out of the scope of this paper. More details can be found here.

The supplier workflow is shown on the left hand side of the diagram below and the customer workflow is
shown on the right. Similar to WF3, the supplier starts with an activating Receive which accepts the
PurchaseOrder and stores it to a workflow variable. You will notice there is no explicit operation
parameter for the customer’s context token. In an improvement over the WF3 model, the context is passed
implicity as part of a callback correlation and the developer does not need to pass the token manually a
parameter in the first Receive. Implemeting the duplex pattern where both service and client can make
calls to each other has been simplified, as we will discuss in detail further on in the paper. After receiving
the PurchaseOrder, the service outputs the data to the command line via a WriteLine activity replacing the
same functionality which was previously implemented in code-behind. A SendReply activity is used to
reply to the customer that the order is received. In the WF3 the supplier then uses a Parallel activity to
ask 3 shippers for quotes on the order. In the WF4 version we replaced that logic with a static
ShippingQuote which is generated by the two Assign activities in the worfklow. The supplier workflow



WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                           Page 7
then uses a Send + ReceiveReply pair to send the quote back to the customer by calling the
OrderDetails operation.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                     Page 8
WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)   Page 9
In the customer workflow, the first set of activities instantiate a PurchaseOrder object to send to the
supplier. Then we use a Send + ReceiveReply pair generated using Add Service Reference to call the
SubmitOrder operation on the service. Then we define Receive + SendReply pair to receive the
ShippingQuote from the supplier. WriteLine activities are used to print out the details of the quote,
instead of the code-behind used in WF3. In this case, a CorrelationScope activity is used to manage the
callback correlation implicitly; the only additional piece of data that the customer workflow needs to
specify is the clientCallbackAddress, which is added to the context binding of the client endpoint in the
app.config file.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                       Page 10
3 Migration guidance

3.1 Contract Design
3.1.1 Defining the service contract
WF3 gives you 2 ways to define the service contract to which the WorkflowService will conform. Both
approaches can be accessed by using the Choose Operation dialog, which appears when you double-click
on a Receive activity.




The first approach is to use the Add Contract button and create a new service contract by adding
operations and configuring their parameters. Using this approach the service contract is generated and
becomes part of the workflow definition. The shape of each operation (parameters, permissions, messaging
pattern) is configured in the Parameters, Properties, and Premissions tabs. The second approach
involves importing an existing service contract defined in code by pressing the Import button.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                     Page 11
In WF4 the contract will be inferred at runtime based on the Receive and SendReply activities that are
present in the workflow, which in turn will control the WSDL projection of the service. The exact shape of
each operation is determined by how the appropriate Receive and SendReply activities are configured, as
we will see below. The Choose Operation dialog box is no longer used. For more information on contract
inference in WF 4, please go here.

In WF3 you could use the Import button in the dialog box in order to import a contract created in code.

Note: WF4 has no mechanism to do this and contracts cannot be reused between workflows. We hope to
add support for this in a future release or service pack.

The Protection Level functionality offered on the WF3 Properties tab is available in WF4 via the
ProtectionLevel property on the Receive activity. The ability to set operation-level permissions via the
Permissions tab in WF3 is not available out of the box in WF4. The user can accomplish service-level
authorization by using WCF’s ServiceAuthorizationManager.

3.1.2 Defining parameters and configuring serialization
In WF3 the parameters of each operation are configured using the Parameters tab in the Choose
Operation dialog or in code if the contract is being imported from code.

In WF4, the Receive activity can be used to accept either Message content or Parameters content.




Message content typically corresponds to a System.ServiceModel.Channels.Message type, or a type
decorated with System.ServiceModel.MessageContractAttribute. Parameters content matches the
method-style programming model where each parameter requires a name and type; each of these types
must be serializable, either by decorating it with the
System.Runtime.Serialization.DataContractAttribute or any of the XmlSerializer attributes in
the System.Xml.Serialization.XmlSerializer namespace. Plain types with no serialization
attributes will also be serialized using DataContractSerializer based on the visibility of their members.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                       Page 12
The Assign To column for each parameter is bound to any WF4 variable, which will be used to store the
result of the Receive.

For additional details on message formatting using the message activities, please go here.

3.1.3 Defining operations
In WF3 all Receive activities create request/reply operations by default, but a one-way operation can be
created by turning on the One Way Operation on the Properties tab of the Choose Operation dialog. If
designing the contract in code, the operation can be made one-way by setting
[OperationContract(IsOneWay = true)].




In WF4 the Receive and Send activities create one-way operations by default.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                         Page 13
To create a request/reply operation in WF4, we need to compose two activities together, in contrast to the
model in WF3. The activity pair will always contain an activity that sends a message and an activity that
receives a message, and the order of these activities will be determined by whether we are implementing
the service or the client. The pairs of activities that are allowed are Receive + SendReply, and Send +
ReceiveReply.

These activity pairs can be used in the designer by using the ReceiveAndSendReply and
SendAndReceiveReply activities in the toolbox.

For simplicity’s sake we will only talk about the Receive + SendReply pair in this section, but the exact
same logic applies in all other cases, even if they are on the client side.

In the WF3 model, in order to implement a service operation, you add child activities to the Receive
activity for that operation. That means the Receive activity in WF3 is a composite activity. In WF4, since
Receive and SendReply are separate “leaf” activities, the activities that implement the operation can be
placed between them (instead of being children of a single Receive activity). This model represents an
improvement over WF3 and provides flexibility in terms of control flow and modeling replies and faults,
especially when using the new Flowchart modeling style.

Since every request/reply operation is now composed of two activities, we can have the situation where
Receive and SendReply activities are not always in pairs, and we need a way to inform the workflow
runtime which activities make up a request/reply pair. To link two activities in a pair, we need to perform a
couple of steps:

   1. Under a common parent for both the Receive and SendReply we need to define a
      Variable<System.ServiceModel.Activities.CorrelationHandle> to ensure the variable is
      in scope for both activities. We name the variable __handle1.
   2. In the Receive we use the CorrelationInitializers collection and reference Handle, and we
      choose the type RequestReplyCorrelationInitializer. This dialog can be accessed by
      accessing the property grid of a Receive activity and clicking the … button next to the label
      CorrelationInitializers.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                         Page 14
   3. Every SendReply activity must have its Request property set to refer to the Receive in the pair.
      Note: if you created the pair using the ReceiveAndSendReply activity template from the toolbox
      or used the right-click & CreateSendReply gesture, then the Request property is already correctly
      configured, as is the correlation handle. The Request property will allow the SendReply to find the
      originating Receive and get access to __handle1, which both activities share.

When using the ReceiveAndSendReply activity template from the designer, it will cause a Sequence to be
created on the canvas, which will contain a Receive and a SendReply. The Sequence will contain a
Variable<CorrelationHandle> called __handle1 which is already correctly configured similarly to
the steps just shown. In addition the Request property of SendReply is also correctly pre-configured. The
same is true if you used the right-click & CreateSendReply gesture.




The same request/reply pair can be implemented in code as shown below:

Variable<CorrelationHandle> Handle = new Variable<CorrelationHandle>();
Variable<double> inputValue = new Variable<double>();

Receive receive = new Receive
{
    DisplayName = "ReceiveAdd",
    OperationName = "Add",
    ServiceContractName = "IService",
    // Content property is elided for simplicity
    CorrelationInitializers =
    {
        new RequestReplyCorrelationInitializer { CorrelationHandle = Handle }




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                      Page 15
      },
};

SendReply reply = new SendReply
{
    DisplayName = "SendReplyAdd",
    // Content property is elided for simplicity
    Request = receive
};




Note: If Receive and SendReply activities always occur sequentially, it is okay to reuse the same handle
(Handle in this case) among all pairs. If they occur in parallel, each pair needs its own unique handle object
in order to avoid clobbering a handle that is already in use.



3.1.4 Working with faults
When defining service operations, we need to account for the possibility of a fault occurring in the service
workflow (caused by something like an exception being thrown) and consider notifying the client workflow
of the fault. Faults can be separated into two categories: undeclared and declared. Undeclared faults are
caused by unexpected exceptions and are usually not propagated to the client, unless the developer
specifically opts in purely for debugging purposes. Declared faults are used when the service author knows
that a given error condition (or exception) can occur. The service author declares a dedicated fault object to
send to the client, and that object contains sufficient information for the client to build logic to react to the
expected error condition. Declared faults are used in production apps, unlike undeclared faults, which are
used just for debugging purposes.

When designing services undeclared faults are handled automatically by the workflow runtime in both
versions of WF. If a service workflow encounters an unhandled exception while processing a request, it will
wrap the exception information in a FaultException or FaultException<ExceptionDetail> type and
send it to the client. The latter type contains more useful debugging information and will be used when
<serviceDebug includeExceptionDetailInFaults="true" /> is configured in the service’s .config
file.

When dealing with declared faults the model varies slightly between WF3 and WF4. In WF3 a fault will be
sent if ReceiveActivity.FaultMessage is bound to FaultException<T> (or just FaultException).
The process for working with faults consists of a few steps:

     1. Declare a fault type (call it TFault)
     2. “Catch” the expected exception using the FaultType property of a FaultHandlerActivity of a
        child activity of the ReceiveActivity (not a FaultHandlerActivity of the ReceiveActivity
        itself). Inside the FaultHandlerActivity create an instance of the type
        FaultException<TFault> that contains relevant information from the exception being caught




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                            Page 16
   3. Set the FaultMessage property of a ReceiveActivity to the instance of
      FaultException<TFault> you just created.

In WF4 the method is slightly different:

   1. Declare a fault type (call it TFault)
   2. Use a TryCatch activity to wrap the activities that implement the operation and the SendReply:
      these are the activities where an expected exception may occur. The SendReply here returns the
      value that results from the operation. In a Catch activity inside the TryCatch, we catch the
      expected exception.
   3. In the body of the Catch activity we add another SendReply activity, where the Request property
      points to the original Receive. The difference from the previous SendReply activity is that the
      Content property of the activity is set to an instance of FaultException<TFault>.

A sample demonstrating the differences between declared faults and undeclared faults can be found here.



3.2 Hosting services
The model for hosting workflow services is largely unchanged in WF4. Instead of using
System.ServiceModel.WorkflowServiceHost like we did in WF3, we use the similar class
System.ServiceModel.Activities.WorkflowServiceHost to host WF4 workflows. The following
table shows the two classes and their public members compared side-by-side.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                     Page 17
System.ServiceModel.WorkflowServiceHost (WF3)                               System.ServiceModel.Activities.WorkflowServiceHost (WF4)
public class WorkflowServiceHost : ServiceHostBase                          public class WorkflowServiceHost : ServiceHostBase
{                                                                           {
                                                                                 public Activity Activity { get; }
                                                                                public DurableInstancingOptions DurableInstancingOptions { get; }
                                                                                public WorkflowInstanceExtensionManager WorkflowExtensions { get; }

     public WorkflowServiceHost(Stream workflowDefinition, params Uri[]         public WorkflowServiceHost(WorkflowService serviceDefinition, params
baseAddress);                                                               Uri[] baseAddresses);
    public WorkflowServiceHost(string workflowDefinitionPath, params            public WorkflowServiceHost(object serviceDefinition, params Uri[]
Uri[] baseAddress);                                                         baseAddresses);
    public WorkflowServiceHost(Type workflowType, params Uri[]                  public WorkflowServiceHost(Activity activity, params Uri[]
baseAddress);                                                               baseAddress);
    public WorkflowServiceHost(Stream workflowDefinition, Stream
ruleDefinition, params Uri[] baseAddress);
    public WorkflowServiceHost(string workflowDefinitionPath, string
ruleDefinitionPath, params Uri[] baseAddress);
    public WorkflowServiceHost(Stream workflowDefinition, Stream
ruleDefinition, ITypeProvider typeProvider, params Uri[] baseAddress);
    public WorkflowServiceHost(string workflowDefinitionPath, string
ruleDefinitionPath, ITypeProvider typeProvider, params Uri[]
baseAddress);

    public ServiceEndpoint AddServiceEndpoint(Type   implementedContract,       public ServiceEndpoint AddServiceEndpoint(Type implementedContract,
Binding binding, string address);                                           Binding binding, string address);
    public ServiceEndpoint AddServiceEndpoint(Type   implementedContract,       public ServiceEndpoint AddServiceEndpoint(Type implementedContract,
Binding binding, Uri address);                                              Binding binding, Uri address);
    public ServiceEndpoint AddServiceEndpoint(Type   implementedContract,       public ServiceEndpoint AddServiceEndpoint(Type implementedContract,
Binding binding, string address, Uri listenUri);                            Binding binding, string address, Uri listenUri);
    public ServiceEndpoint AddServiceEndpoint(Type   implementedContract,       public ServiceEndpoint AddServiceEndpoint(Type implementedContract,
Binding binding, Uri address, Uri listenUri);                               Binding binding, Uri address, Uri listenUri);
                                                                                public ServiceEndpoint AddServiceEndpoint(XName serviceContractName,
                                                                            Binding binding, string address, Uri listenUri);
                                                                                public ServiceEndpoint AddServiceEndpoint(XName serviceContractName,
                                                                            Binding binding, Uri address, Uri listenUri);


}                                                                           }




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                     Page 18
When constructing the service host, we need to provide two pieces of information: (a) the workflow
definition that we want to host and (b) the service configuration information for hosting the workflow (base
address, service behaviors, endpoints, etc). In WF3 we provide workflow definition in the
WorkflowServiceHost constructor by pointing to the type of a coded workflow, or a Stream or file path
that points to a declarative workflow. The host attempts to load the service configuration from the
application .config file, looking for a <service> tag where the name attribute matches the name of the
workflow class. If the endpoint information is not provided in configuration, endpoints can be added in
code by using the WorkflowServiceHost.AddServiceEndpoint() method.

In WF4 the model is slight different – we can either provide a WorkflowService with the workflow set as
its .Body property, or the Activity definition which contains the workflow as its implementation (either a
class that derives from Activity, or a XAML <Activity> definition that has an x:class attribute). The
WorkflowServiceHost will look for a default <service> tag in the application .config file, where the name
of that <service> tag matches the WorkflowService.ConfigurationName property (when a
WorkflowService is provided), or the DisplayName of the root activity of the workflow definition (if no
WorkflowService is provided). The WF4 host also has a special AddServiceEndpoint() method which
allows users to add endpoints programmatically that match the XName specified in the
Receive.ServiceContractName.

The contents of the <service> tag specified in the application .config file will not vary between WF3 and
WF4 and the service configuration can be reused. All bindings that could be used in WF3 can now be used
in WF4. There are a lot of other commonalities between the two hosts. The host lifetime is also managed
identically in both cases, by using its Open() and Close() methods. The Description collection gives you
access to the WCF service description (which is generated automatically in WF4 as part of the contract
inference step) and the Extensions collection lets you access the service host extensions. WF4 also
enables a new extensibility point called WorkflowExtensions. These extensions can be either per
instance or shared for the entire set of instances. For most information on WorkflowExtensions, please
go here.

A commonly used feature of the host in WF3 is the ability to get access to the
System.ServiceModel.Description.WorkflowRuntimeBehavior service behavior inside the
Description.Behaviors collection. This behavior enables you to get direct access to the
WorkflowRuntime object and provides rich extensibility. In WF4, in order to get access to some events
related to the execution of a workflow instance, you can implement a custom
WorkflowHostingEndpoint. For more information about WorkflowHostingEndpoint, please go here.
Alternatively, you can get access to a full range of events by using a
System.Activities.Tracking.TrackingParticipant. The TrackingParticipant is then added to
the WorkflowExtensions collection. For additional details on configuring Tracking, please go here.

3.3 Configuring correlation
As a workflow executes, the next activity scheduled to execute and any variables defined in the workflow
represent the state of that workflow instance. If multiple clients start accessing the same workflow service,
multiple instances of the same workflow may be created, each with its own state (think about a loan
application service, where each loan application instance might be at a different processing stage


WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                         Page 19
depending on the information the user has provided). A client needs a reliable way to be able to route
messages to the same instance that was created when it first accessed the service. Since workflows may be
long-running, we cannot rely on the session maintained on the transport level since those sessions are
short-lived and transient. Workflow needs to maintain its own notion of a session.

The mechanism that is available in WF3 is called context-based correlation. Using this mechanism the
service issues a context token to the client and returns it along with the result of that first client call. The
client passes that token along with every subsequent call so the workflow runtime knows which service the
message needs to be routed to. Think about buying an item at a store. When you complete a purchase you
can think of there being an underlying workflow. You are given a receipt which you can later use to access
that same workflow instance in case you want to later resume the workflow and perhaps make a return or
exchange of the item you purchased.

Context exchange is enabled by a set of three bindings, similar to some regular WCF bindings, but with the
added ability to exchange the context token. These bindings are BasicHttpContextBinding,
WSHttpContextBinding, and NetTcpContextBinding.

The same context-based correlation mechanism described above (complete with the context bindings) is
available in WF4. Since the bindings are the same, the wire protocol between WF3 and WF4 is compatible.
Conceptually the context exchange model stays exactly the same, but there are some differences in the way
it is implemented using the activities available out of the box. WF4 supports a new content-based
correlation method, but discussing that is beyond the scope of this document. For resources on content-
based correlation, please see this document.

3.3.1 Simple messaging patterns
First, let’s discuss the case where we are dealing with a strict service and client, where the client only sends
requests to the service and the service listens for those requests and replies to them. In WF3 the context
exchange is abstracted away from the user. When a workflow service/client workflow is configured with
one of the three context bindings, the context channel starts managing the context exchange under the
covers. In the service, on any activating (where CanCreateInstance is true) Receive the service knows
to return the context token to the client along with the response. The activating Receive needs to be a
request/reply operation for this to work. In the client workflow, if a context token is received, the client
channel will know to include that context token with any subsequent requests to the service. The following
diagram shows a possible message exchange. The first step is the context exchange “handshake”, after
which any messaging pattern can be supported.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                            Page 20
                                   Client                                 Service



                     Send                                                           Receive
                 (request/reply)                                                 (request/reply,
                                                                                   activating)
                                       “handshake” - required first step



                       Send                                                            Receive
                     (one way)                                                        (one way)



                      Send                                                             Receive
                  (request/reply)                                                   (request/reply)




The pattern above works implicitly in WF3 without the user needing to configure the related activities in
any special way. WF4 follows the same fundamental pattern and also handles it implicitly by using a
CorrelationScope activity.

3.3.2 Complex messaging patterns
Let’s now proceed to a more complex pattern, where the notions of a server and a client become blurred. In
this scenario both parties can initiate message exchanges, not just the client. Said slightly differently, both
the client and the service can make use of the Receive activity to listen for messages addressed to them.

The duplex pattern relies on the same notion of context tokens like the strict client/server pattern, but the
context exchange protocol varies slightly. In order for the service to be able to call the client back, the client
needs to supply to the service a context token, which the service can attach to outgoing requests to the
client, so the workflow runtime knows which client instance the message needs to be dispatched to. In
essence, this is the inverse of the pattern we saw in strict client/server case, with the roles of the two
workflows reversed. Thus at the end of the duplex context exchange, both service and client have a token
they can use to call back the other party.

The implementation of this notion using the WF3 activity model requires that the workflow author
manually provide the client’s context token to the service, usually by making it an operation parameter in
the Receive that activates the service workflow. Once the service has obtained the client context token it
stores it and attaches it to any Send operations bound for the client. This pattern is shown in the following
diagram where the client and service context tokens are differentiated by a subscript letter.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                                  Page 21
                                    Client                                Service



                                                                                   Receive
                     Send                        C
                                        (as parameter)                          (request/reply,
                 (request/reply)
                                                                                  activating)
                                                                      S

                                        “handshake” - required first step


                     Receive                                          C                 Send
                  (request/reply)                                                   (request/reply)


                                                 S
                      Send                                                             Receive
                  (request/reply)                                                   (request/reply)




The pattern above works in WF3 but requires some work on the part of the user. First, the service
activating Receive operation needs to have one extra parameter which is used to send the context token.
The user has to manually store this object and use it to set the Context property of any Send activities that
will communicate with the client. On the client, the user needs to be able to generate the context token to
send to the service. This is accomplished by looking at the first Receive operation that will be called by the
service and sending the value of its Context property to the service.

In WF4, the context protocol is extended to include a notion of “client context” via a type of correlation
called Callback Correlation. The client context is sent as part of the initiating message so that the service
knows how to call back to the one who initiated the exchange. This is especially useful in cases where the
interaction between the two parties is long-running, hence why it has also been dubbed “durable duplex”.
For additional information on this pattern, please go here.

The conversations messaging pattern available in WF3 is not covered in the current version of this paper
but may be covered in a future version.

3.4 Building the client




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                                  Page 22
When building the client in WF3, you would drop Send activities into the designer and then configure the
endpoint and binding the client should use to communicate with the service in the application .config file.
Then you would use the ChannelToken property on every Send activity to associate the two pieces. Then
you had to configure every send activity to match an operation on the service. To do that you would
double-click a Send activity and use the Choose Operation dialog (shown in section 3.1.1) to import the
contract from the service you built. This process has been simplified in WF4 by adding support for the Add
Service Reference command in workflows. Once Add Service Reference is invoked and pointed to a
running service, it will generate pre-configured custom activities that let you talk to the service. Note that
under the covers, the generated custom activity contains a Send + ReceiveReply pair. The appropriate
client endpoint configuration elements are also created and the Send is configured to refer to them.




Context-based correlation is configured using these generated activities by wrapping them in a
CorrelationScope activity. The CorrelationScope handles all of the context header manipulation
needed to work with both context-based correlation and callback correlation.

Note: After invoking Add Service Reference, you can see the generated activities in your Solution Explorer
by clicking the Show All Files option and then expanding out the generated service reference. You can then
double-click the generated custom activities and edit them in the designer, if you so choose.




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                          Page 23
If the service we are working with uses declared faults, we can construct logic in the client to handle these
faults. As part of the Add Service Reference step, the TFault type discussed in 0 will be generated in the
client. In WF3 we would have used a FaultHandlerActivity to catch FaultException<TFault>, but in
WF4 we can use the analogous TryCatch activity.

Traditionally we use Add Service Reference in the client workflow to generate activities that send
messages to the service. In the duplex messaging case discussed in section 3.3.2 the roles of the service and
client become somewhat blurred since either can originate messages to the other. In this case we may use
Add Service Reference in our service workflow to generate activities to call the client, or simply use a
Send + ReceiveReply activity pair directly.

3.5 Hosting the client
In WF3 we could use the WorkflowRuntime to host the client of a workflow service. The same is true in
WF4, where we can use WorkflowApplication to fulfill the same functionality, or even
WorkflowInvoker if the client workflow is short-running. Any workflow that does not use the WF4
Receive activities (using ReceiveReply is okay) can be hosted using WorkflowApplication. If a
workflow uses Receive, then we need to use the WorkflowServiceHost to host it.

In WF3 we add the ChannelManagerService to the WorkflowRuntime in order to host clients, but that is
no longer necessary with WorkflowApplication.

Below is a code snippet that shows a common hosting scenario using WorkflowInvoker:




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                          Page 24
WorkflowInvoker.Invoke(new SequentialCalculatorClient());
Console.ReadKey();

If the client implements a duplex messaging pattern (in other words if the Receive activity is used), then
WorkflowApplication or WorkflowInvoker cannot be used and we need to use
WorkflowServiceHost as shown in section 0. This is the same behavior as WF3.

When WorkflowServiceHost is opened, the underlying workflow is not started automatically, but the
workflow runtime waits for an activating Receive or similar activity. If we are hosting a client workflow
that implements a duplex messaging pattern, the workflow probably does not start with a Receive activity,
so just opening the host will not cause the workflow to run. WF3 allows us to work around this behavior
and start the duplex client via code similar to the snippet below.

WorkflowServiceHost host = new WorkflowServiceHost(typeof(CustomerWorkflow));
host.Open();
WorkflowInstance workflow =
host.Description.Behaviors.Find<WorkflowRuntimeBehavior>().WorkflowRuntime.CreateWo
rkflow(typeof(CustomerWorkflow));
workflow.Start();

This mechanism is not available in WF4, and we need a different way to start the client duplex workflow.
We use the notion of a creation endpoint: a web service endpoint that we add to WorkflowServiceHost,
which can create instances of the hosted workflow definition. This is a regular endpoint that can receive
messages from external callers, or can be called locally by the client itself. The following code snippet
shows how this is done. The code relies on the IWorkflowCreation interface, which is available in the
Conversations solution which accompanies this paper.

// add a creation endpoint so we can create instances of the Customer WF
CreationEndpoint creationEp = new CreationEndpoint(new
      NetNamedPipeBinding(NetNamedPipeSecurityMode.None), new
      EndpointAddress("net.pipe://localhost/customer_creationEndpoint"));
host.AddServiceEndpoint(creationEp);

// in this case, the Customer WF is started using a CreationEndpoint
IWorkflowCreation client = new ChannelFactory<IWorkflowCreation>
     (creationEp.Binding, creationEp.Address).CreateChannel();
client.Create(null);




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                        Page 25
The information contained in this document relates to pre-release software product, which may be substantially modified before its
first commercial release. Accordingly, the information may not accurately describe or reflect the software product when first
commercially released. This document is provided for informational purposes only, and Microsoft makes no warranties, express or
implied, with respect to this document or the information contained in it.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter
in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does
not give you any license to these patents, trademarks, copyrights, or other intellectual property.



Microsoft, Windows, Visual Studio, and the .NET logo are trademarks of the Microsoft group of companies. All other trademarks are
property of their respective owners




WF 4 Migration Cookbook: Workflow Services (.NET 4 - Beta 2 Release)                                               Page 26

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:52
posted:1/16/2012
language:Latin
pages:26
Description: Sample Simple Workflow document sample