任务 2:创建工作流服务客户端

在此任务中,您要创建一个基本工作流服务客户端,该客户端调用通过您在任务 1:创建工作流服务中创建的工作流服务定义并实现的操作。该客户端在控制台窗口中显示它传递给每个数学运算调用的值以及在响应中发送回来的服务。

提示

该客户端将在本教程的其余练习中使用。

提示

使用 Visual Studio 工作流设计器创建或管理工作流服务时,该设计器有时会产生虚假的验证错误。如果可以成功地生成项目,请忽略验证错误。

创建工作流服务客户端

  1. 如果当前没有打开 WorkflowServiceTutorial 解决方案,请打开 Visual Studio 2008,单击**“文件”,突出显示“打开”**,并导航到 WorkflowServiceTutorial 解决方案。

  2. 如果 WcfSvcHost.exe 未运行,请按 Ctrl + F5 生成并运行 WorkflowServiceTutorial 服务。必须运行此服务才能完成本任务中的步骤。在**“服务”列表中右击“WorkflowServiceTutorial.ServerWorkflow”,然后选择“复制元数据地址”[Copy Metadata Address]**。

  3. 单击**“文件”,突出显示“添加”,然后选择“新建项目”**。

  4. 在**“新建项目”对话框中的“工作流”下,选择“顺序工作流控制台应用程序”**。

  5. 将项目命名为**“WorkflowServiceClient”** ,然后单击**“确定”**。

  6. 在**“解决方案资源管理器”中右击“WorkflowServiceClient”节点,然后选择“添加服务引用…”**

    将在上一步复制的元数据地址复制到**“添加服务引用”对话框上的“地址”框中,然后单击“转到”。当“ServerWorkflow”出现在“服务”框中时,单击“确定”**添加服务引用。

  7. 为您的工作流打开工作流设计器页,并使用**“工具箱”**窗格将 SendActivity 活动添加到您的工作流。

  8. 在工作流设计器中突出显示 SendActivity 活动。

  9. 导航到**“属性”窗格,在“ServiceOperationInfo”属性下单击省略号以打开“选择操作”**对话框。

  10. 在右上角,单击**“导入”**。

  11. 在**“类型”选项卡下,突出显示“<当前项目>”**。

  12. 从类型列表中选择“IWorkflow1”,然后单击**“确定”**。

    **“选择操作”**对话框中将填充有关协定和操作(由模板定义并由您在 WorkflowServiceTutorial 项目中实现)的信息。

  13. 在**“可用操作”下,突出显示“StartupService”并单击“确定”**。

    设计器将 SendActivity 活动与 StartupService 操作关联,方法是:创建 TypedOperationInfo 对象,用协定和操作信息填充该对象,并通过 ServiceOperationInfo 属性将它与您的 SendActivity 活动关联。

  14. 在工作流设计器中,导航到**“属性”**窗格。

  15. ChannelToken 属性中,为 ChannelToken 对象创建名称并按 Enter。

  16. 展开 ChannelToken 属性。

  17. 对于 EndpointName 属性,请导航到客户端的 App.config 文件。复制并粘贴要用于访问服务的终结点的名称。例如,在以下代码中,终结点名称为“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>
    

    提示

    App.config 文件中的 EndpointName 属性 (Property) 和名称属性 (Attribute) 必须相同,否则,运行客户端应用程序时您会收到错误。

  18. 对于 OwnerActivityName 属性,将它保留为空。如果不选择名称,则编译时会为您选择根活动的名称。

  19. 在**“属性”窗格中,单击“事件”**按钮。

  20. AfterResponse 事件的文本框中双击,以生成事件处理程序。

  21. 输入以下代码以在调用 StartupService 操作后向用户显示以下消息。

    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. 在工作流设计器中,再添加五个 SendActivity 活动。

  23. 将每个 SendActivity 活动与一个操作以及您在步骤 13-17 中使用的相同客户端通道令牌相关联。

  24. 因为数学运算(Add、Subtract、Multiply、Divide)采用一个名为 n1int,所以您必须为 n1 创建活动绑定或为它设置值。在本教程中,我们将绑定到工作流中的现有属性。为此,请打开 Workflow1.cs(如果您创建了 Visual Basic 解决方案,则打开 Workflow1.vb)并执行下列步骤:

    1. 将 Workflow1 类重命名为 ClientWorkflow。

    2. ClientWorkflow 类定义中,创建一个公共属性,如下面的示例所示:

      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. 打开工作流设计器,然后在与调用 Add 操作相关的 SendActivity 活动的**“属性”窗格中,将“inputValue”绑定到“n1”活动属性,方法是:单击省略号,然后在“绑定到现有成员”选项卡中选择“inputValue”**。

    4. 实现 BeforeSend 事件的事件处理程序,以创建要发送到服务的消息。

      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. 若要显示服务返回的结果,需要创建一个变量以在操作成功时保存返回值。为此,请打开 Workflow1.cs(如果您创建了 Visual Basic 解决方案,则打开 Workflow1.vb)并执行下列步骤:

    1. ClientWorkflow 类定义中,创建一个名为 returnedValue 的公共属性,如下面的示例所示。

      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. 打开工作流设计器,并在**“属性”窗格中将“returnedValue”绑定到“(ReturnValue)”活动属性,方法是:单击省略号,然后在“绑定到现有成员”选项卡中选择“returnedValue”**。

    3. 实现 AfterResponse 事件的事件处理程序,以查看服务发送回来的消息。

      private void sendActivity2_AfterResponse(object sender, SendActivityEventArgs e)
      {
          Console.WriteLine("The value after invoking the Add operation is {0}", returnedValue); 
      }
      
  26. 对于其他每一个数学运算,分别将**“inputValue”“returnedValue”绑定到“n1”“(ReturnValue)”**。

  27. 在与数学运算相关联的其余每个 SendActivity 活动中实现 BeforeSendAfterResponse 事件的事件处理程序,就像在步骤 24c 和 25c 中一样。下面的示例演示所有事件处理程序的实现,包括与 ShutdownService 操作关联的 SendActivity 活动的事件处理程序。

        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. 在 Program.cs(在 Visual Basic 中为 Module1.vb)中,将 ChannelManagerService 添加到 WorkflowRuntime 用于缓存通道和通道工厂的服务列表。是否使用 ChannelManagerService 是可选的,但是,如果不使用它,将不会缓存通道,并且当与服务进行通信时,工作流中的每个 SendActivity 活动都会使用新的通道实例。

    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. 若要在通过练习 1 创建的 WorkflowServiceTutorial 解决方案中配合使用客户端与工作流服务,必须从 WorkflowServiceTutorial 项目属性中删除命令行参数。为此,请执行下列步骤:

    1. 右击 WorkflowServiceTutorial 项目节点,然后选择**“属性”**。
    2. 选择**“调试”选项卡,然后在详细信息窗格中的“启动选项”**下,移除文本框中的 /client:"WfcTestClient.exe"。
  30. 右击 WorkflowServiceTutorial 解决方案节点,然后选择**“属性”**。

  31. 在“属性页”对话框中,选择**“多启动项目”**。

  32. 如果 WorkflowServiceTutorial 未作为顶级项列在列表中,请使用列表框侧面的箭头将它设为顶级项。您需要执行此操作,以便该服务在客户端应用程序尝试对它调用任何操作之前开始运行。

  33. 对于列表中的每个项目,将操作从**“无”更改为“启动”**。

  34. 单击**“应用”,然后单击“确定”**。

  35. 如果您已经创建了 Visual Basic 解决方案,则在**“解决方案资源管理器“窗格中右击 WorkflowServiceClient 项目节点,然后选择“属性”**。

  36. 选择**“应用程序”选项卡,然后从“根命名空间”**文本框中移除 WorkflowServiceClient。如果不这样做,您将无法连接到工作流服务,因为客户端将引用错误的命名空间。

  37. 生成并运行工作流服务解决方案。

  38. 在服务启动以后,客户端应用程序将会运行。从命令提示符处运行客户端应用程序时,您将看到以下内容。

    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.
    

另请参见

其他资源

练习 1:创建基本工作流服务

版权所有 (C) 2007 Microsoft Corporation。保留所有权利。