Task 2: Create a Workflow Service Client

Download sample

In this task, you create a basic workflow service client that invokes the operations defined and implemented in the workflow service you created in Task 1: Create the Workflow Service. The client displays in a console window what value it passed into each math operation invocation and what the service sent back in response.

Note

This client is used in the remaining exercises in this tutorial.

Note

When the Visual Studio workflow designer is used to create or manage workflow services, it sometimes produces spurious validation errors. If you are able to successfully build your project, ignore the validation errors.

To create the workflow service client

  1. If you do not currently have the WorkflowServiceTutorial solution open, then open Visual Studio 2008, click File, highlight Open, and navigate to the WorkflowServiceTutorial solution.

  2. If WcfSvcHost.exe is not running, press Ctrl + F5 to build and run the WorkflowServiceTutorial service. This service must be running to complete the steps in this task. Right-click WorkflowServiceTutorial.ServerWorkflow in the Service list and choose Copy Metadata Address.

  3. Click File, highlight Add, and select New Project.

  4. In the New Project dialog box, under Workflow, select Sequential Workflow Console Application.

  5. Name your project WorkflowServiceClient and click OK.

  6. Right-click the WorkflowServiceClient node in Solution Explorer and choose Add Service Reference…

    Paste the metadata address that you copied a previous step into the Address box on the Add Service Reference dialog and click Go. Once ServerWorkflow appears in the Services box, click OK to add the service reference.

  7. Open the workflow designer page for your workflow and add a SendActivity activity to your workflow using the Toolbox pane.

  8. Highlight the SendActivity activity in the workflow designer.

  9. Navigate to the Properties pane and under the ServiceOperationInfo property, click the ellipsis to open the Choose Operation dialog box.

  10. In the upper-right corner, click Import.

  11. Under the Type tab, highlight <Current Project>.

  12. Select IWorkflow1 from the type list and click OK.

    The Choose Operation dialog box will populate with information about the contract and operation that was defined by the template and implemented by you in the WorkflowServiceTutorial project.

  13. Under Available Operations, highlight StartupService and click OK.

    The designer associates the SendActivity activity with the StartupService operation by creating a TypedOperationInfo object, populating it with contract and operation information, and associating it with your SendActivity activity through the ServiceOperationInfo property.

  14. In the workflow designer, navigate to the Properties pane.

  15. In the ChannelToken property, create a name for the ChannelToken object and press ENTER.

  16. Expand the ChannelToken property.

  17. For the EndpointName property, navigate to the App.config file for the client. Copy and paste the name for the endpoint you want to use to access the service. For example, in the following code, the endpoint name is "WSHttpContextBinding_IWorkflow1."

    <client>
        <endpoint address="https://localhost:8080/ServerWorkflow" binding="wsHttpContextBinding"
            bindingConfiguration="WSHttpContextBinding_IWorkflow1" contract="ServiceReference.IWorkflow1"
            name="WSHttpContextBinding_IWorkflow1">
            <identity>
                <userPrincipalName value="someone@example.com" />
            </identity>
        </endpoint>
    </client>
    

    Note

    The EndpointName property and the name attribute in your App.config file must be the same or you will receive errors when running your client application.

  18. For the OwnerActivityName property, leave it blank. By not selecting a name, the name of the root activity will be selected for you at compilation time.

  19. In the Properties pane, click the Events button.

  20. Double-click in the text box for the AfterResponse event to generate an event handler.

  21. Enter the following code to display the following message to the user after the StartupService operation has been invoked.

    Private Sub sendActivity1_AfterResponse(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            Console.WriteLine("A service instance has successfully been created.")
        End Sub
    
    private void sendActivity1_AfterResponse(object sender, SendActivityEventArgs e)
    {
        Console.WriteLine("A service instance has successfully been created.");
    }
    
  22. In the workflow designer, add five more SendActivity activities.

  23. Associate each SendActivity activity with one operation and the same client channel token you used in steps 13-17.

  24. Because the math operations (Add, Subtract, Multiply, Divide) take an int named n1, you must either create an activity binding for n1 or set a value for it. In this tutorial, we will bind to an existing property in our workflow. To do that, open Workflow1.cs (or Workflow1.vb if you created a Visual Basic solution) and perform the following steps:

    1. Rename the Workflow1 class to ClientWorkflow.

    2. In your ClientWorkflow class definition, create a public property as shown in the following example:

      Public class ClientWorkflow
          Inherits SequentialWorkflowActivity
      
          Public inputValue As Integer = Nothing
      
          Private Sub sendActivity1_AfterResponse(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
              Console.WriteLine("A service instance has successfully been created.")
          End Sub
      End Class
      
      public sealed partial class ClientWorkflow: SequentialWorkflowActivity
      {
          public int inputValue = default(int);
      
          public ClientWorkflow()
          {
              InitializeComponent();
          }
      
          private void sendActivity1_AfterResponse(object sender, SendActivityEventArgs e)
          {
              Console.WriteLine("A service instance has successfully been created.");
          }
      }
      
    3. Open the workflow designer, and in the Properties pane for the SendActivity activity associated with invoking the Add operation, bind inputValue to the n1 activity property by clicking on the ellipsis and selecting inputValue in the Bind to an existing member tab.

    4. Implement an event handler for the BeforeSend event to create the message to send to the service.

      Private Sub sendActivity2_BeforeSend(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
          inputValue = 1
          Console.WriteLine("The initial input value is {0}", inputValue)
      End Sub
      
      private void sendActivity2_BeforeSend(object sender, SendActivityEventArgs e)
      {
          inputValue = 1;
          Console.WriteLine("The initial input value is {0}", inputValue);
      }
      
  25. To display the results back from the service, you need to create a variable to hold the return value when the operation succeeds. To do that, open Workflow1.cs (or Workflow1.vb if you created a Visual Basic solution) and perform the following steps:

    1. In your ClientWorkflow class definition, create a public property named returnedValue as shown in the following example.

      Public class ClientWorkflow
          Inherits SequentialWorkflowActivity
      
          Public inputValue As Integer = Nothing
          Public returnedValue As Integer = Nothing
      ...
      End Class
      
      public sealed partial class ClientWorkflow: SequentialWorkflowActivity
      {
          public int inputValue = default(int);
          public int returnedValue = default(int);
      
          public ClientWorkflow()
          {
              InitializeComponent();
          }
      ...
      }
      
    2. Open the workflow designer, and in the Properties pane, bind returnedValue to the (ReturnValue) activity property by clicking on the ellipsis and selecting returnedValue in the Bind to an existing member tab.

    3. Implement an event handler for the AfterResponse event to view the message sent back by the service.

      private void sendActivity2_AfterResponse(object sender, SendActivityEventArgs e)
      {
          Console.WriteLine("The value after invoking the Add operation is {0}", returnedValue); 
      }
      
  26. For each of the other math operations, bind inputValue and returnedValue to n1 and (ReturnValue), respectively.

  27. Implement event handlers for the BeforeSend and AfterResponse events in each remaining SendActivity activity associated with a math operation as you did in steps 24c and 25c. The following example shows the implementation for all event handlers, including the one for the SendActivity activity associated with the ShutdownService operation.

        Private Sub sendActivity1_AfterResponse(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            Console.WriteLine("A service instance has successfully been created.")
        End Sub
    
        Private Sub sendActivity2_BeforeSend(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            inputValue = 1
            Console.WriteLine("The initial input value is {0}", inputValue)
        End Sub
    
        Private Sub sendActivity2_AfterResponse(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            Console.WriteLine("The value after invoking the Add operation is {0}", returnedValue)
        End Sub
    
        Private Sub sendActivity3_BeforeSend(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            inputValue = 2
            Console.WriteLine("The new input value is {0}", inputValue)
        End Sub
    
        Private Sub sendActivity3_AfterResponse(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            Console.WriteLine("The value after invoking the Subtract operation is {0}", returnedValue)
        End Sub
    
        Private Sub sendActivity4_BeforeSend(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            inputValue = 6
            Console.WriteLine("The new input value is {0}", inputValue)
        End Sub
    
        Private Sub sendActivity4_AfterResponse(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            Console.WriteLine("The value after invoking the Multiply operation is {0}", returnedValue)
        End Sub
    
        Private Sub sendActivity5_BeforeSend(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            inputValue = 3
            Console.WriteLine("The new input value is {0}", inputValue)
        End Sub
    
        Private Sub sendActivity5_AfterResponse(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            Console.WriteLine("The value after invoking the Divide operation is {0}", returnedValue)
        End Sub
    
        Private Sub sendActivity6_AfterResponse(ByVal sender As System.Object, ByVal e As System.Workflow.Activities.SendActivityEventArgs)
            Console.WriteLine("The workflow service instance has been successfully shut down.")
        End Sub
    
        private void sendActivity1_AfterResponse(object sender, SendActivityEventArgs e)
        {
            Console.WriteLine("A service instance has successfully been created.");
        }
    
        private void sendActivity2_BeforeSend(object sender, SendActivityEventArgs e)
        {
            inputValue = 1;
            Console.WriteLine("The initial input value is {0}", inputValue);
        }
    
        private void sendActivity2_AfterResponse(object sender, SendActivityEventArgs e)
        {
            Console.WriteLine("The value after invoking the Add operation is {0}", returnedValue);
        }
    
        private void sendActivity3_BeforeSend(object sender, SendActivityEventArgs e)
        {
            inputValue = 2;
            Console.WriteLine("The new input value is {0}", inputValue);
        }
    
        private void sendActivity3_AfterResponse(object sender, SendActivityEventArgs e)
        {
            Console.WriteLine("The value after invoking the Subtract operation is {0}", returnedValue);
        }
    
        private void sendActivity4_BeforeSend(object sender, SendActivityEventArgs e)
        {
            inputValue = 6;
            Console.WriteLine("The new input value is {0}", inputValue);
        }
    
        private void sendActivity4_AfterResponse(object sender, SendActivityEventArgs e)
        {
            Console.WriteLine("The value after invoking the Multiply operation is {0}", returnedValue);
        }
    
        private void sendActivity5_BeforeSend(object sender, SendActivityEventArgs e)
        {
            inputValue = 3;
            Console.WriteLine("The new input value is {0}", inputValue);
        }
    
        private void sendActivity5_AfterResponse(object sender, SendActivityEventArgs e)
        {
            Console.WriteLine("The value after invoking the Divide operation is {0}", returnedValue);
        }
    
        private void sendActivity6_AfterResponse(object sender, SendActivityEventArgs e)
        {
            Console.WriteLine("The workflow service instance has been successfully shut down.");
        }
    
  28. In Program.cs (or Module1.vb in Visual Basic), add the ChannelManagerService to the list of services used by the WorkflowRuntime to cache channels and channel factories. Using the ChannelManagerService is optional, but if you do not use it, the channels will not be cached and every SendActivity activity in a workflow will use a new channel instance when communicating with the service.

    Shared WaitHandle As New AutoResetEvent(False)
    
    Shared Sub Main()
        Using workflowRuntime As New WorkflowRuntime()
            AddHandler workflowRuntime.WorkflowCompleted, AddressOf OnWorkflowCompleted
            AddHandler workflowRuntime.WorkflowTerminated, AddressOf OnWorkflowTerminated
    
            ' Add ChannelManagerService to the list of services used by the WorkflowRuntime.
            Dim cms As ChannelManagerService = New ChannelManagerService()workflowRuntime.AddService(cms)
    
            Dim workflowInstance As WorkflowInstance
            workflowInstance = workflowRuntime.CreateWorkflow(GetType(ClientWorkflow))
            workflowInstance.Start()
            WaitHandle.WaitOne()
        End Using
    End Sub
    Shared Sub OnWorkflowCompleted(ByVal sender As Object, ByVal e As WorkflowCompletedEventArgs)
        Console.WriteLine("The client workflow has completed." + vbLf + "Press <Enter> to exit the client application.")
        Console.ReadLine()
        WaitHandle.Set()
    End Sub
    
    Shared Sub OnWorkflowTerminated(ByVal sender As Object, ByVal e As WorkflowTerminatedEventArgs)
        Console.WriteLine(e.Exception.Message)
        WaitHandle.Set()
    End Sub
    
    using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
    {
        AutoResetEvent waitHandle = new AutoResetEvent(false);
    
        // Add ChannelManagerService to the list of services used 
        // by the WorkflowRuntime.
        ChannelManagerService cms = new ChannelManagerService();    workflowRuntime.AddService(cms);
    
        workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) 
        {
            Console.WriteLine("The client workflow has completed. \nPress <Enter> to exit the client application."); 
            Console.ReadLine(); 
            waitHandle.Set(); 
        };
    
        workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
        {
            Console.WriteLine(e.Exception.Message);
            waitHandle.Set();
        };
    
        WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowServiceClient.ClientWorkflow));
        instance.Start();
    
        waitHandle.WaitOne();
    }
    
  29. To use the client with your workflow service in the WorkflowServiceTutorial solution created in Exercise 1, you must delete a command-line argument from the WorkflowServiceTutorial project properties. To do so, perform the following steps:

    1. Right-click the WorkflowServiceTutorial project node and select Properties.

    2. Select the Debug tab, and in the detail pane under Start Options, remove /client:"WfcTestClient.exe" from the text box.

  30. Right-click the WorkflowServiceTutorial solution node and select Properties.

  31. In the Property Pages dialog box, select Multiple startup projects.

  32. If WorkflowServiceTutorial is not listed as the top item in the list, use the arrows on the side of the list box to make it the top item. This is needed so that your service starts running before your client application tries to invoke any operations on the service.

  33. For each project in the list, change the action from None to Start.

  34. Click Apply and then click OK.

  35. If you have created a Visual Basic solution, then in the Solution Explorer pane, right-click the WorkflowServiceClient project node and select Properties.

  36. Select the Application tab and remove WorkflowServiceClient from the Root Namespace text box. If you do not do this, then you will not be able to connect to the workflow service because the client will be referencing the wrong namespace.

  37. Build and run the workflow service solution.

  38. After the service has started, the client application runs. You should observe the following when running your client application from the command prompt.

    A workflow service instance has successfully been created.
    The initial input value is 1
    The value after invoking the Add operation is 1
    The new input value is 2
    The value after invoking the Subract operation is -1
    The new input value is 6
    The value after invoking the Multiply operation is -6
    The new input value is 3
    The value after invoking the Divide operation is -2
    The workflow service instance has been successfully shut down.
    The client workflow has completed. 
    Press <Enter> to exit the client application.
    

See Also

Other Resources

Exercise 1: Create a Basic Workflow Service

Copyright © 2007 by Microsoft Corporation. All rights reserved.
Last Published: 2010-03-04