What You Will Learn in This Hour:
Overview of Windows Communication Foundation
Exposing WF workflows as WCF services
Accessing WCF services from WF workflows
Directly Accessing the WF runtime from the WCF host
Before jumping directly into how to use Windows Communication Foundation (WCF) with WF, this section provides an overview of WCF and the next provides an overview of using the two together.
WCF and WF are complementary products that are both part of the .NET 3.0 and 3.5 Frameworks. WCF is Microsoft’s distributed computing technology. It is the preferred way to provide and access network endpoints on the Microsoft platform. WCF subsumes all previous Microsoft distributed technologies, including web services, web service enhancements, .NET remoting, and enterprise services. WCF supplies the most thorough web service standard support on the Microsoft platform. WCF can listen for and access network endpoints via HTTP, TCP, named pipes, and just about any other communication protocol.
In the past, enterprise services and .NET remoting were frequently the best choices when communicating behind the firewall, whereas web services were generally best when communicating across the firewall. This required using different programming and security models based on the communication pattern. If the service had both cross-firewall and local clients, this frequently required creating web service and .NET enterprise service or .NET remoting endpoints as well. Even when using web services, you could choose between standard and employing web service enhancement technology (WSE) to gain additional security.
WCF rationalizes distributed computing on the Microsoft platform by providing one programming model and one runtime. It separates the service (the functionality) from the endpoint. The endpoint contains the information necessary to communicate on the wire. A service communicating across the firewall can choose the WCF HTTP binding, which sends standard SOAP over HTTP using basic security. A service also requiring additional security can use the sibling HTTP binding that includes WS* security. Finally a service communicating behind the firewall can utilize the TCP binding that uses compiled SOAP. Therefore, the same service can support all three communication patterns. There needs to be one WCF endpoint created for each communication pattern the service supports. WCF can also use MSMQ when queuing is necessary; it is just another binding. Finally, to streamline endpoint support even more, WCF binding information can be placed in configuration files, allowing the wire format to change without requiring recompilation.
WCF can be hosted in any .NET 2.0 or later application domain, such as a Windows Service, IIS, or Windows Activation Service (improved IIS in Windows 2008) process. The ability to listen across just about any protocol securely and reliably, in any Windows process, and to do so at maximum efficiency makes WCF very powerful. WCF abstracts service functionality from wire format and hosting requirements. There really is no reason not to use WCF for distributed computing on the Microsoft platform. Because of WCF’s adept endpoint capabilities, it is commonly referred to as Microsoft’s service-oriented technology, with the endpoints being the entry points to the services. As you will see, WF or standard .NET types can provide the logic behind the endpoint.
Workflows frequently need to be exposed across the network and to access other services on the network. WF can leverage WCF as the endpoint while it provides the logic component of the network service. A customer service, for example, may be exposed as a WCF endpoint and also call out to a credit service. In this case, the WF workflow can be exposed as a WCF endpoint and can utilize WCF to call the credit service as well. WCF is WF’s conduit to and from the outside world when working with distributed systems.
WCF provides two activities and hosting capabilities to WF in .NET 3.5 (Figure 19.1. The Send
activity provides functionality similar to both the CallExternalMethod
activity and the InvokeWorkflow
activity. The Receive
activity is similar to the HandleExternalEvent
activity (and the response portion of the InvokeWorkflow
activity). Each activity can be associated with an interface and configured to support synchronous or asynchronous operations. The Send
activity, for example can call a method on a service or workflow (that is exposed as a service), pass in parameters, and receive a response, just as a standard method can. The Receive
activity provides the same capabilities in the other direction, it receives responses rather than requesting them.
Having all the flexibility of method calls baked into WF activities that can be used to call services and workflows is quite powerful. It is certainly an improvement over .NET 3.0, where calling to and from the host is not optimum. Workflow to host must be synchronous and host to workflow must use asynchronous events and a special EventArgs
-derived payload. Calling workflows is even harder in .NET 3.0, especially to do so synchronously. By and large, the communicating to and from workflows is greatly improved in .NET 3.5. The communication is, however, a work in progress. Correlation (Hour 8, “Working with Parallel
Activities and Correlation”) is not particularly straightforward to implement at the current time with WorkflowServices.
The third main component of WF-WCF integration is WCF hosting. The WCF Receive
activity works in conjunction with the WorkflowServiceHost
type to host WF workflows and to create endpoints. These endpoints are called WorkflowServices. When placing a Receive
activity on a WF workflow, WCF (or the WorkflowServiceHost
type) must host the workflow. Built-in capabilities simplify hosting WCF (and WorkflowServices) in IIS and WAS (IIS in Vista and Windows Server 2008). WCF can also be hosted in Windows Services, Console applications, Windows Forms applications, and other .NET processes, although it is still up to you to manage the lifetime in these applications.
Because of the capability of the Send
and Receive
activities, the IIS and WAS hosting advantages, and the apparent emphasis toward WCF as a hosting technology going forward, it is reasonable to consider hosting all WF applications as WorkflowServices. Another reason not to host WF in WCF is when using another dedicated WF host product, such as SharePoint or MS CRM. In these cases WCF can still be used to access endpoints, but they are the workflow hosts.
In .NET 3.0, WF and WCF must be integrated manually. No WorkflowServiceHost
type or Send
and Receive
activities exist. Manual integration is not covered in this book.
There are two workflow-specific projects in the 3.5 Framework. The first contains a sequential workflow project and a WCF contract (interface). The second holds state machine workflows and an accompanying contract. These project types are relevant when exposing a workflow as a service.
The first two exercises in this hour are part of hosting a WF workflow in WCF. They are broken into smaller steps to help digest them.
You begin this hour hosting a workflow in WCF (creating a WorkflowService). You perform this in two steps. First, you use the interface and Receive
activity prepopulated with the Sequential Workflow Service Library template. This allows you to create functional WorkflowService with minimal work. Then you modify the interface and reconfigure the existing Receive
activity to use the new interface. The combination of the two parts demonstrates all the standard steps to create a functional WorkflowService.
Next, you learn to access the WF runtime form the WorkflowServiceHost
. By default, the WF runtime is loaded as part of the WorkflowServiceHost
under the hood. You will need to access it many times—for example, to register events and add runtime services. Finally, you will call another workflow from a workflow using the Send
activity.
WCF is an extremely large subject. Its coverage is limited to that necessary for it to host and be called from WF. If you want more detail on WCF, see Essential Windows Communication Foundation (WCF): For .NET Framework 3.5 (Addison-Wesley, Steve Resnick, Richard Crane, and Chris Bowen), MSDN, or other sources.
In this exercise you create a WCF endpoint that hosts a WF workflow. You create a client application to access the WCF host (that runs the workflow). You will leverage the interface and Receive
activity created by the WCF Sequential Workflow Service Library project to reduce the steps necessary to see a WF workflow run in WCF.
This solution contains three projects. The first holds the WF workflow and contract. The second holds the WCF host, and the third holds the client.
Follow the next steps to create the solution.
1. Start Visual Studio 2008. Select File, New, Project.
2. Expand the Project Types and select Other Project Types.
3. Select the Visual Studio Solutions project template.
4. Enter WcfHostsWfSolution
as the Name.
5. Enter or browse to C:SamsWf24hrsHoursHour19LearningWF-WCFIntegration
for the Location.
6. Click OK.
Follow the next steps to create a Sequential Workflow Service Library project that will hold a workflow ready to be hosted in WCF.
Follow the next steps to create the console host and console client project.
WCF endpoints contain an address, a binding, and a contract. The address is the location of the service. The Sequential Workflow Service Library project template created the following address for the Workflow1 service to facilitate testing: http://localhost:8731/Design_Time_Addresses/ContractsAndWorkflows/Workflow1/. Open the App.config
file in the ContractsAndWorkflows
project and you will see this address in the baseAddresses
element. A couple of lines below, the binding is set to wsHttpContextBinding
and the contract to ContractsAndWorkflows.IWorkflow1
. By leaving the address null, it is created by adding the service name to the baseAddress
.
The bindings dictate the method the service listens for and how it interacts with clients. Change the binding and the service can communicate over a new protocol. You will see at the end of this topic that new bindings were created to permit WCF to host long-running workflows. These bindings permit WCF to retain context across WF workflow persistence points. This exemplifies the power of WCF bindings. They dictate much more than protocols.
These three elements are commonly referred to as the ABCs of WCF. For a WCF client to connect to a WCF host, they must both have identical ABCs. That is, the client connects to the service at the address, over the binding, and via the contract the service specifies. WCF ships with support for a number of bindings. These include the BasicHttpBinding, WsHttpBinding,
and NetTcpBinding
bindings. The difference between the BasicHttpBinding
and the WsHttpBinding
is that the latter includes web services security and reliable message support under the WS* umbrella of standards.
Custom WCF bindings can be created when none of the out-of-the-box WCF bindings work for your scenario. When WCF hosts WF, it needs to maintain session across the workflow persists and activations. Three custom bindings permit WF session state to be managed from WCF: BasicHttpContextBinding, WsHttpContextBinding
, and NetTcpContextBinding
. As you can see by the name, one is for standard http, another is for WS*-compliant http, and the last is for TCP.
The ContractsAndWorkflows
project contains a WCF interface, a workflow that implements it, and a configuration file that specifies the endpoint information.
The interface (IWorkflow1.cs
) has a ServiceContract
attribute, and its GetData
operation is decorated with an OperationContract
attribute (see the next code snippet). The ServiceContract
attribute informs WCF that this interface can be used to create WCF services. The OperationContract
attribute states that this operation should be available to service consumers. Operations can exist in a WCF interface that are not available as WCF service operations. Only the operations with OperationContract
attribute are available. Also, note the standard interface convention. No mandate requires that a synchronous method be used for workflow-host communication and that an asynchronous event be used for host-workflow communication. Any combination of synchronous and asynchronous operations can be used. In this case, a standard synchronous operation delivers the complete roundtrip communication between workflow and host.
The workflow created by the Sequential Workflow Service Library Project template is prepopulated with a Receive
activity. The Receive
activity is used when workflows are hosted in WCF. The Receive
activity works in conjunction with the WorkflowServiceHost
type. The WorkflowServiceHost
type is used when WCF endpoints host WCF workflows. The operations exposed by the Receive
activity and data it exchanges with clients are specified in an interface.
Follow the next steps to explore the Receive
activity and its properties.
1. Open the workflow in design mode.
2. Click the Receive
activity and look at its properties (Figure 19.3).
Let’s examine each of the Receive
activity’s nongeneric properties starting with ServiceOperationInfo
and then going from the top down:
ServiceOperationInfo
—Binds the Receive
activity to a service contract (interface) operation. As you can see, it is bound to the GetData
operation from the IWorkflow interface that was created with the project.
ReturnValue
—The value returned to the client when the Receive
activity completes execution. The Receive
activity is a composite activity, as will be demonstrated in the next section.
ContextToken
—This is used for correlation purposes when the client contacts multiple Receive
activities on the same workflow. This token ensures that subsequent calls access the proper instance of the workflow. The concept is identical to the CorrelationToken
used on CallExternalMethod
and HandleExternalEvent
activities covered in Hour 8.
FaultMessage
—Allows a fault message to be sent to the client if a problem arises. Returning fault messages is the standard way to return an error to a client when web services are used.
OperationValidation
—Validation logic to be executed before message is accepted. One possibility is to perform role checking.
Value
—The parameter specified in the interface.
The ContextToken, FaultMessage,
and OperationValidation
properties are not covered in this book. The others are used in this hour.
Click the workflow (not the Receive
activity), look in the property window, and you will see the WorkflowServiceAttributes
property. It is a new property that is promoted to the workflow when a Receive
activity is added. It contains a number of WCF-centric properties, including one that specifies whether exception details are included in faults. These properties are not covered anymore, but you should make a mental note for when you might need them.
As mentioned, the Receive
activity is a composite activity. All activities placed in it will be executed before it completes and returns a value to the client.
Follow the next steps to see the default properties and to place a Code
activity into the Receive
activity.
1. Open the workflow in code view and you will see ReturnValue
and InputValue
properties that were bound to the Receive
activity ReturnValue
and value
properties.
2. Place a Code
activity in the Receive
activity. Double-click it and add the following code to its handler:
// Set the return value based on the customer number
ReturnValue = InputValue == 1 ? "Good" : "Bad";
The workflow is shown in Figure 19.4. The Code
activity evaluates the value passed in and returns either good or bad based on the value received.
Open the App.config
file. There is a lot of XML noise. The WCF content is held in the system.serviceModel
element. The Services
element contains the service name and a behaviorConfiguration
attribute, which I will explain shortly. The baseAddress
plus the address attribute of the endpoint element holds the service address. In this case the address attribute is blank, so the entire address is held in the baseAddress
. The identity element holds the identity and comments you should read. Finally, the next endpoint element holds the address to the service metadata. This is where the WSDL can be accessed and will be used to create proxy and an App.config
file later.
<system.serviceModel>
<services>
<service name="ContractsAndWorkflows.Workflow1"
behaviorConfiguration="ContractsAndWorkflows.Workflow1Behavior">
<host>
<baseAddresses>
<add
baseAddress="http://localhost:8731/Design_Time_Addresses/ContractsAndWorkflows/Wo
rkflow1/" />
</baseAddresses>
</host>
<endpoint address=""
binding="wsHttpContextBinding"
contract="ContractsAndWorkflows.IWorkflow1">
<!-- Upon deployment, the following identity element should be removed
or replaced to reflect the identity under which the deployed service runs. If
removed, WCF will infer an appropriate identity automatically.-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
The behavior element contains other information about the service, such as permitting HTTP Get to be used to access the metadata, which allows it to be viewed in a browser.
<behaviors>
<serviceBehaviors>
<behavior name="ContractsAndWorkflows.Workflow1Behavior" >
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<windowsAuthentication
allowAnonymousLogons="false"
includeWindowsGroups="true" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Services can be configured in code or via configuration file. The advantage of using a configuration file is that it can be changed without recompiling the service, and potentially done so by administrators. You will use the configuration file created by the WCF Sequential Workflow Service Library project template when creating the host in the next section
WCF, like WF, can be hosted in any .NET 2.0 or later app domain. The ServiceHost type, contained in the System.ServiceModel
namespace, is the gateway to WCF, much like the WorkflowRuntime
type is the gateway to WF functionality. When WCF hosts a workflow, the WorkflowServiceHost
type, contained in the System.WorkflowServices
namespace, is used in place of the ServiceHost
type. The WorkflowServiceHost
type contains a handle to the WF runtime, thereby permitting a WCF host to run a WF workflow, register runtime events, and add runtime services. The WCF endpoint will be hosted in a Console Application.
You will implement a WorkflowServiceHost,
set up an endpoint, and allow metadata to be accessed. The endpoint information will be extracted from the App.Config
file in the workflow project.
Follow the next steps to add references to the assemblies needed to host a WF workflow in WCF. Then reference the project holding the workflow to be run.
1. Click the ConsoleApplicationWcfWorkflowHost
project in the Solution Explorer.
2. Add references to System.ServiceModel, System.Workflow.Activities, System.Workflow.ComponentModel
, and System.WorkflowServices
.
3. Add a reference to the ContractsAndWorkflows
project.
4. Open Program.cs
and add the following using
directives:
using System.ServiceModel;
using System.ServiceModel.Description;
The WorkflowServiceHost
needs to know where to listen (address) and what to listen for (contract or type). All this information will be obtained from the configuration file.
Follow the next steps to move the App.config
file from the workflow project into the console host. Then add code to the Program.cs
file to instantiate the WorkflowServiceHost
.
1. Cut the App.config
file from the ContractsAndWorkflows
project and copy it into the ConsoleApplicationWcfWorkflowHost
project.
2. Now it is time to instantiate the WorkflowServiceHost
type and to pass it the type (the workflow). Add the following code to the top of the Main
method in Program.cs
in the ConsoleApplicationWcfWorkflowHost
project:
// Instantiate the WorkflowServiceHost and pass it the
// workflow (type) to invoke.
WorkflowServiceHost selfWorkflowHost =
new
WorkflowServiceHost(typeof(ContractsAndWorkflows.Workflow1));
Follow the next steps to open the host to start it listening and build the project. The workflow will run in its context. Then you will pause the host while it waits. This must be done, just as with WF, because this is a console application and the console application will complete if not blocked while running workflows (or waiting for workflows to run) that run asynchronously, which is the default.
1. Add the following code to open the host, to pause it, and finally to close it:
// Open the service so that it starts listening for client calls.
selfWorkflowHost.Open( );
// Pause the host while it waits for clients to invoke
// workflow through it.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine( );
Console.ReadLine( );
// Close the service.
selfWorkflowHost.Close( );
2. Build the ConsoleApplicationWcfWorkflowHost
project.
The host can now be started and the endpoint can begin listening. The WCF host must be running before it can be referenced by the client you will create in the next section, so leave it running when you’ve completed this section.
Follow the next steps to configure the order the projects should start in and then run the project.
We will use the multiple startup project option because Visual Studio 2008 is required to use the functionality in this hour.
1. Right-click the solution in the Solution Explorer, select Properties, and click the plus sign to expand Common Properties. Select Startup Project, and select the Multiple Startup projects option in the middle of the dialog. Select the ConsoleApplicationWcfWorkflowHost
project in the list of projects, click the arrow to the right of it, and select Start from the drop-down. Then click OK.
2. Press F5 to start the project (all projects specified to start). You should see the host has started and is waiting to receive a client request that it will, in turn, pass onto the workflow as shown in Figure 19.5.
3. Terminate the ConsoleApplicationWcfWorkflowHost
.
There are two steps to creating the client. The first is referencing the WCF host to build a proxy and a configuration file. The second step is to add the logic to the client to instantiate and invoke the method on the workflow proxy.
Follow the next steps to add referencing and a using
directive.
The client needs to reference the service (much the same as a client to an ASMX web service needs to reference the web service). The client may be on the same computer or access the service across the Internet.
Follow the next steps to add a service reference from the client to the host using the Svcutil.exe
command line utility.
You can also add a client reference to a service by right-clicking the project and choosing Add Service Reference (similar to adding an ASMX web service reference) or by using the Svcutil.exe
utility. The first is a little easier but does not offer much control. Therefore, we will use Svcutil.exe
to add service references in this book.
1. Press F5 to start running the ConsoleApplicationWcfWorkflowHost
.
2. Select Start, All Programs, Visual Studio 2008, Visual Studio Tools, and Visual Studio 2008 Command Prompt.
3. Enter cdSamsWf24hrsHoursHour19LearningWF-WCFIntegrationWcfHostsWfSolutionConsoleApplicationClient
to go to the client directory.
4. Enter the following command to generate a proxy and a configuration file in the current directory. The HTTP address is taken from the baseAddress
element of the App.config
file in the ContractsAndWorkflows
project and must match the address where the service runs. (Remember the host must be running before you reference it.)
svcutil.exe /language:cs /out:GeneratedProxy.cs /config:app.config
http://localhost:8731/Design_Time_Addresses/ContractsAndWorkflows/Workflow1/
5. Two files, one named GeneratedProxy.cs
and the other App.config
should have been created.
6. Stop the host project from running. You must do this to include the file in the project in a couple of steps.
7. Go back to Visual Studio and click the ConsoleApplicationClient
project in the Solution Explorer.
8. Click the Show All files icon and the just generated files; a couple of directories should appear in a dimmed format (Figure 19.6). It is a toggle, so click again if you do not see the files.
9. Select the GeneratedProxy.cs
and the App.config
files. Then right-click and choose Include in Project. They should no longer be dimmed.
The default behavior of SvcUtil
is to create a new ServiceModel
element if there is an existing App.Config
file and to create a new one otherwise. However, a command-line option exists (mergeConfig)
that will merge the new endpoints into an existing ServiceModel
element. You can explore numerous options by entering SvcUtil
from the command prompt.
The GeneratedProxy.cs,
as the name implies, is a proxy. It is used to call the service. The App.config
file contains the service endpoint configuration details. Follow the next steps to look at the generated files.
1. Open the GeneratedProxy.cs
file. Skip down to the public partial class Workflow1Client
. This is the class you will instantiate in the client, and its GetData
operation will access the method on the workflow Receive
activity.
2. Open the App.config
file. The client’s App.config
file contains bindings and client elements. The binding element specifies the binding to connect to the service—wsHttpContextBinding
in this case. Other binding-related properties exist, such as the timeout value.
3. The Client
element at the bottom of the file contains the address, binding, and contract to use to contact the service. These values must match the service values for the client to connect.
Now that the client has a reference to the service, follow the next steps to instantiate the proxy and call the service, which will, in turn, call the workflow.
1. Open Program.cs
in the ConsoleApplicationClient
project.
2. Add the following code to instantiate an object to call the service, which then invokes the workflow. The address, binding, and contract are retrieved from the configuration file.
// Instantiate the WorkflowInterfaceClient from the proxy.
// Endpoint information is retrieved from the config file.
Workflow1Client client = new Workflow1Client( );
3. Add the following code to call the workflow’s GetData
method, again through the service.
// Call the GetData method on the workflow
string status = client.GetData(1);
Console.WriteLine("the status is: " + status);
4. Pause the host:
Console.WriteLine("Press Enter to terminate the client.");
Console.Read( );
5. The completed client Program.cs Main
method should look like this:
static void Main(string[ ] args)
{
// Instantiate the WorkflowInterfaceClient from the proxy.
// Endpoint information is retrieved from the config file.
Workflow1Client client = new Workflow1Client( );
// Call the GetData method on the workflow
string status = client.GetData(1);
Console.WriteLine("the status is: " + status);
Console.WriteLine("Press Enter to terminate the client.");
Console.Read( );
}
6. Build the ConsoleApplicationClient
project.
The service should still be running. If not, start it so that the client can access it. Follow the next steps to configure the client project to start in the Multiple Startup projects choice and run the solution.
1. Right-click the solution in the Solution Explorer, select Properties, and click the plus sign to expand Common Properties. Select Startup Project, select the Multiple Startup projects option in the middle of the dialog. Select the ConsoleApplicationClient
project in the list of projects, click the arrow to the right of it, and select Start from the drop-down.
2. Click the ConsoleApplicationWcfWorkflowHost
project and click the up arrow to the right of the list until it is the first project in the list to ensure it starts first. Then click OK.
3. Press F5 to start the projects. You should see that the client ran and retrieved the status from the workflow (Figure 19.7).
4. Stop both the client and host projects.
In the previous exercise, you created a workflow, host, and client, and then connected them all. You leveraged the Receive
activity, interface, and service created with the Sequential Workflow Service Library project template. In this exercise, you modify the interface, delete the existing Receive
activity, add a new Receive
activity, and configure the new Receive
activity.
WCF interfaces are decorated with ServiceContract
and their members with OperationContract,
as well as other attributes that delineate they are available to interact with clients. Follow the next steps to decorate the IWorkflow
interface as appropriate.
1. Open the IWorkflow.cs
file in the ContractsAndWorkflows
project.
2. Replace the current GetData
method with the following GetStatus
method (leave the attribute):
string GetStatus(string customer);
3. Delete the remaining interface content. The IWorkflow1
interface should now look like this:
[ServiceContract]
public interface IWorkflow1
{
[OperationContract]
string GetStatus(string customer);
}
There should be a red exclamation error mark on the Receive
activity, as the method it points to no longer exists. Follow the next steps to delete the existing Receive
activity and configure a new one. This will allow you to start from the beginning.
1. Open the workflow in code view and delete the code that initializes the properties and variables. The only code remaining in the class should be the constructor and the code activity handler as shown:
public sealed partial class Workflow1 : SequentialWorkflowActivity
{
public Workflow1( )
{
InitializeComponent( );
}
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
// Set the return value based on the customer number
ReturnValue = InputValue == 1 ? "Good" : "Bad";
}
}
2. Move the Code
activity from Receive
activity to the workflow.
3. Delete the existing Receive
activity, and add a new Receive
activity to the workflow.
4. Move the Code
activity into the Receive
activity you just added.
5. Click on the Receive
activity. Then click on its ServiceOperationInfo
property and click the ellipsis.
6. The Choose Operation dialog is spawned (Figure 19.8).
7. Select the Import icon in the upper-right corner.
8. The Browse and Select a .NET Type dialog is displayed. Expand Current Project, select ContractsAndWorkflows,
and choose IWorkflow1
in the middle pane. Click OK.
9. You are returned to the Choose Operation dialog, where the GetStatus
method shows (Figure 19.9). Only one method is in the interface, so you can click OK. If there were multiple methods, you would choose which one to apply to this Receive
activity.
10. Click the CanCreateInstance
property and set it to True
. This tells WF to start a new instance when a message for this Receive
activity is received. If there were multiple Receive
activities, the subsequent Receive
activities would not invoke a new workflow instance. They would instead use an existing instance.
11. Bind the ReturnValue
property to a new field. Click the ReturnValue
property. Click the ellipsis, select the Bind to a New Member tab, select the Create Field option button, name it ReturnValue
, and choose OK.
12. Bind the customer property to a new field. Click the customer property. Click the ellipsis, select the Bind to a New Member tab, select the Create Field option button, name it Customer
, and click OK.
13. Modify the Code
activity handler to compare to a string rather than an int by replacing it with the following code:
// Set the return value based on the customer number
ReturnValue = Customer == "1" ? "Good" : "Bad";
14. Stop the host if it is still running.
15. Build the entire solution.
The Receive
activity (therefore service implementation) and interface are now updated.
In this section, you will update the proxy and change the client to invoke the new method as well.
The client proxy is not pointed at the GetStatus
method. It is still pointed at the GetData
method contained in the original contract. Follow the next steps to run SvcUtil
again to update the proxy to point to the correct operation. Then start the host project manually because you do not want all projects to execute.
1. Delete the GeneratedProxy.cs
and the App.config
files from the ConsoleApplicationClient
project by right-clicking them and selecting Delete.
2. Start the host by going to the C:SamsWf24hrsHoursHour19LearningWF-WCFIntegrationWcfHostsWfSolutionConsoleApplicationWcfWorkflowHostinDebug
directory in Windows Explorer and double-clicking the ConsoleApplicationWcfWorkflowHost.exe
file.
3. Select Start, All Programs, Visual Studio 2008, Visual Studio Tools, and Visual Studio 2008 Command Prompt.
4. Enter cdSamsWf24hrsHoursHour19LearningWF-WCFIntegrationWcfHostsWfSolutionConsoleApplicationClient
to go to the client directory.
5. Enter the following command to generate a proxy and a configuration file in the current directory.
svcutil.exe /language:cs /out:GeneratedProxy.cs /config:app.config
http://localhost:8731/Design_Time_Addresses/ContractsAndWorkflows/Workflow1/
6. Terminate the host project. You must do this to include the file in a couple of steps.
7. Go back to Visual Studio and click the ConsoleApplicationClient
project in the Solution Explorer.
8. New versions of the GeneratedProxy.cs
and the App.config
should have been created. If the files are not shown, click the Show All Files icon once or twice until they appear.
9. Select the GeneratedProxy.cs
and the App.config
files. Then right-click and choose Include in Project. They should no longer be dimmed.
10. Go to the bottom of the GeneratedProxy.cs
file and make sure it is invoking the GetStatus
method and not the GetData
method. If not, build the solution again, and then go back to step 1 to start over.
Follow the next steps to update the client to call the GetStatus
method.
1. Open Program.cs
in the ConsoleApplicationClient
project and replace the code that calls the GetData
method and outputs the results with the following:
// Call the GetData method on the workflow
string status = client.GetStatus("1");
Console.WriteLine("the status is from the status method is: " +
status);
2. Build the ConsoleApplicationClient
project.
3. Press F5 to run the solution. The client should access the new updated workflow, and you should see the result shown in Figure 19.10.
4. Stop the console projects.
Even though you have not interacted with the runtime, the workflows in this hour have been run by the WF runtime. The WorkflowServiceHost
has managed this interaction behind the scenes. At times, however, you need more control of the way workflows are hosted. Registering events with the runtime and adding runtime services, for instance, requires access to the runtime.
In this section, you modify the ConsoleApplicationWcfWorkflowHost
host application to retrieve the WorkflowRuntime
from the WorkflowServiceHost
. Then you register the completed and terminated events and add the tracking service. The only new code is to retrieve the WorkflowRuntime
from the WorkflowServiceHost
. Registering events and adding services works the same as it does when accessing the WorkflowRuntime
directly.
Follow the next steps to add references, using
directives, and perform other prepatory work.
1. Click the ConsoleApplicationWcfWorkflowHost
project in the Solution Explorer.
2. Add a reference to the WorkflowRuntime
.
3. Add the following using
directives to Program.cs
in the ConsoleApplicationWcfWorkflowHost
project:
using System.Workflow.Runtime;
using System.Workflow.Runtime.Tracking;
4. Add the following member variable below the opening class bracket:
static string connectionString = "Initial
Catalog=WFTRackingAndPersistence;" +
"Data Source=localhost; Integrated Security=SSPI;";
Follow the next steps to access the WF runtime from WCF using WCF’s extensibility capability. The WorkflowRuntime
is retrieved from the WorkflowServiceHost.Description.Behaviors
collection property. Accessing the WF runtime this way conforms to standard WCF extensibility.
1. Add the following code below the line of code that instantiates the WorkflowServiceHost
to retrieve the WorkflowRuntime
from the WorkflowServiceHost
type:
// Retrieve the WorkflowRuntime from the WorkflowServiceHost
WorkflowRuntime workflowRuntime =
selfWorkflowHost.Description.Behaviors.Find
<WorkflowRuntimeBehavior>( ).WorkflowRuntime;
2. Add the following code below the code you just added to register the events and add runtime services:
// Register the workflow events
workflowRuntime.WorkflowCompleted +=
new EventHandler<WorkflowCompletedEventArgs>
(workflowRuntime_WorkflowCompleted);
workflowRuntime.WorkflowTerminated +=
new EventHandler<WorkflowTerminatedEventArgs>
(workflowRuntime_WorkflowTerminated);
// Add the sql tracking service to the runtime
SqlTrackingService sts = new
SqlTrackingService(connectionString);
workflowRuntime.AddService(sts);
3. Add the following handlers below the Main
method:
static void workflowRuntime_WorkflowCompleted
(object sender, WorkflowCompletedEventArgs e)
{
Console.WriteLine("WorkflowServiceHost accessed runtime
completed hander. ");
}
static void workflowRuntime_WorkflowTerminated
(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine("Workflow terminated");
}
4. Build the ConsoleApplicationWcfWorkflowHost
project.
Follow the next steps to run the project, and ensure that the WorkflowCompleted
handler emitted the content specified in it to the console.
1. Press F5 to run the solution.
2. Go to the host console, and you should see that the workflow completed handler was invoked as shown in Figure 19.11.
3. If you would like, run the WorkflowMonitor (Hour 5, “Creating an Escalation Workflow”) or look in the database directly to see that the workflow is being tracked.
4. Stop both the client and host projects.
Now it is time to use the Send
activity to access a workflow. The workflow with the Send
activity will replace the current client that calls the workflow and receives its results. When you use the Send
activity to invoke a service, the workflow does not have to run under a WCF host. Moreover, if there are no Receive
activities on the workflow, it must not be hosted by the WorkflowServiceHost
. Therefore, we must host our new client workflow in the standard WF runtime, where it will access the host workflow that determines the status.
The client workflow accesses the host workflow via its address, bindings, and contract. It can access any endpoint capable of supporting these characteristics. Other possible endpoints include standard WCF services not enabled by workflows and standard web services.
Before continuing, the major Send
activity properties are the ServiceOperationInfo
property that binds the Send
activity to an interface, just as it does for the Receive
activity, and the ChannelToken
property that points to the EndpointName
attribute in the client section of the App.config
file. AfterSend
and BeforeSend
are handlers that can be called before and after the message is sent. Logging and preparation are common uses of these handlers.
In this section, you add a new workflow and configure it to use the Send
activity.
Follow the next steps to create a Sequential Workflow Console Application project to host the client workflow.
The workflow will initialize a variable in the initial Code
activity that will be passed to the service as a parameter. The second Code
activity will then emit the results returned from the service to the console.
Follow the next steps to add the activities to the workflow.
Follow the next steps to configure the ServiceOperationInfo
property, which is almost the same as configuring this property on the Receive
activity. You will also bind a few properties to send a value to and receive a value from the service.
1. Add a reference to the ContractsAndWorkflows
project.
2. Click the Send
activity. Then click its ServiceOperationInfo
property and click the ellipsis.
3. The Choose Operation dialog is spawned.
4. Select the Import icon in the upper-right corner.
5. The Browse and Select a .NET Type dialog is displayed. Expand the ContractsAndWorkflows
project, and choose IWorkflow1
in the middle pane. Click OK.
6. You are returned to the Choose Operation dialog. The GetStatus
operation is selected because it is the only operation. Click OK to exit.
7. Bind the ReturnValue
property to a new field. Click the ReturnValue
property. Click the ellipsis, select the Bind to a New Member tab, select the Create Field option button, name it StatusValueReturned,
and click OK. This stores the status returned from the called workflow.
8. Bind the customer property to a new field. Click the value property. Click the ellipsis, select the Bind to a New Member tab, select the Create Field option button, name it CustomerValueSent
, and click OK.
The ChannelToken
property holds the endpoint name of the service the Send
activity will invoke. Follow the next steps to populate it with the endpoint name attribute from the client section of the App.config
file. This maps the client service parameters (address, binding, and contract) to the Send
activity. This is needed because multiple client services may be in one config file. We will use the generated name here, the binding plus the contract, but you are free to change it to a more meaningful name, such as OrderServiceEndpoint
.
1. Copy the App.Config
file from the ConsoleApplicationClient
project to the ClientWorkflow
project.
2. Open the App.config
file in the ClientWorkflow
project. Go down to the client section.
3. Copy the text between the quotes in the name attribute in the client endpoint element (WSHttpContextBinding_IWorkflow1
) and paste it in NotePad for use in a couple of steps.
4. The contract attribute in the App.config
file must include the project name. To do so, prepend ContractsAndWorkflows
to the contract attribute. It is directly to the left of the name attribute. It should look like this when complete:
contract="ContractsAndWorkflows.IWorkflow1"
5. Click the ChannelToken
property. Enter TheChannelToUse
. Then press Enter.
6. Click the + next to the ChannelToken
property. Paste the following text you copied from the App.config
file in the EndpointName
property:
WSHttpContextBinding_IWorkflow1
7. Click the drop-down in the OwnerActivityName
property and select Workflow1.
Follow the next steps to update the Code
activities handlers.
1. Double-click the first Code
activity and add the following code to its handler:
CustomerValueSent = "1";
2. Double-click the second Code
activity and add the following code to its handler:
Console.WriteLine("The service returns the following: " +
StatusValueReturned);
3. Build the ClientWorkflow
project.
Follow the next steps to reconfigure the projects to start in the correct order. Then run the solution and the host will return the status to the workflow client.
1. Right-click the solution in the Solution Explorer, select Properties, and click the plus sign to expand Common Properties. Select Startup Project and select the Multiple Startup projects option in the middle of the dialog. Select the ConsoleApplicationClient
project in the list of projects, click the arrow to the right of it, and select None from the drop-down. Now set the ClientWorkflow
project to start. Then reorder the projects and make sure the ConsoleApplicationWcfWorkflowHost
project is first in the list.
2. Press F5 to start the solution; you should see the results shown in Figure 19.12 in the client host Window:
In this hour you learned that WCF is complementary to WF. The first is the endpoint and latter the logic. It allows endpoints to be instantiated and called securely, reliably, and at optimum speed. WF can supply application logic and long-running support to WCF endpoints. Alternatively, WF can call out to WCF endpoints. This synergy is likely to increase, with WCF becoming WF’s de facto host.
18.217.15.45