任务 1:创建工作流服务

在本任务中,将创建一个实现 Windows Communication Foundation (WCF) 协定的基本的状态机工作流服务,该服务的状态信息保存到 SQL 数据库中。这将通过“协定优先”创作样式来完成。

提示

本教程中的其余练习将使用这个工作流服务。

提示

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

设置工作流服务并定义协定

  1. 打开 Visual Studio 2008,单击**“文件”,选择“新建”,然后选择“项目”**。

  2. 在**“新建项目”对话框中的“WCF”下,选择“状态机工作流服务库”**模板。

  3. 将您的项目命名为 WorkflowServiceTutorial,然后单击**“确定”**。

    Visual Studio 2008 将为您的工作流服务生成下列文件:一个 App.config 文件(用来存储配置设置,所用架构与 WCF 服务所使用的架构相同)、一个源文件(其中包含协定的定义)和多个工作流类源文件(为工作流定义设计器代码和实现代码)。

    该模板将创建一个具有两个 StateActivity 活动的状态机工作流。第一个 StateActivity 活动的名称为 Workflow1InitialState,其中包含的 EventDrivenActivity 活动本身包含一个后跟有 SetStateActivity 活动的 ReceiveActivity 活动。该 SetStateActivity 活动的 TargetStateName 属性设置为状态机工作流中另一个 StateActivity 活动的名称。

    ReceiveActivity 类型本身派生自 SequenceActivity,因此,该类型中可以包含多个按顺序执行的子活动。实现 ReceiveActivity 活动与针对标准 WCF 服务实现某个操作(而不是在方法体中实现您的代码)并行执行,您将通过 WF 活动来实现操作。

    本教程中的 ReceiveActivity 活动实现了几个在 IWorkflow1.cs(如果您创建的是 Visual Basic 解决方案,则为 IWorkflow1.vb)中定义的新操作。这些操作是请求/响应操作;因此,客户端将接收从服务发回的消息。

    由于您使用的是已经提供的 WCF 协定,因此这称作用来创作工作流服务的“协定优先”**样式。如果要以编程方式创建新协定,请参见How to: Implement a Windows Communication Foundation Contract Operation

  4. 由于您打算针对您的接口定义新操作,因此请打开 IWorkflow1.cs(如果您创建的是 Visual Basic 解决方案,则为 IWorkflow1.vb),并将现有的接口定义替换为如下示例中的代码:

    <ServiceContract()> _
    Public Interface IWorkflow1
    
        <OperationContract()> _
        Sub StartupService()
    
        <OperationContract()> _
        Function Add(ByVal n1 As Integer) As Integer
    
        <OperationContract()> _
        Function Subtract(ByVal n1 As Integer) As Integer
    
        <OperationContract()> _
        Function Multiply(ByVal n1 As Integer) As Integer
    
        <OperationContract()> _
        Function Divide(ByVal n1 As Integer) As Integer
    
        <OperationContract()> _
        Sub ShutdownService()
    
    End Interface
    
    [ServiceContract]
    public interface IWorkflow1
    {
    
        [OperationContract]
        void StartupService();
    
        [OperationContract]
        int Add(int n1);
    
        [OperationContract]
        int Subtract(int n1);
    
        [OperationContract]
        int Multiply(int n1);
    
        [OperationContract]
        int Divide(int n1);
    
        [OperationContract]
        void ShutdownService();
    }
    

实现服务协定

  1. 如果工作流设计器不可见,请通过右击“Workflow1.cs”(如果您创建的是 Visual Basic 解决方案,则为 Workflow1.vb)来打开该设计器,并选择**“视图设计器”**。

  2. 将一个 StateActivity 活动拖动到状态机工作流中,并将其重命名为 OperationsState。

  3. 将 stateActivity1 重命名为 FinalState。

    提示

    当您重命名此活动时,它的工作流状态不再标记为已完成;因此,请在工作流设计器中右击“FinalState”并选择“设置为已完成状态”

  4. 在名为 Workflow1InitialState 的 StateActivity 活动中,选择 eventDrivenActivity1 并将其重命名为 WaitToStartService。

  5. 双击“WaitToStartService”将其展开。ReceiveActivity 活动即已设置为 WaitToStartService 的子活动。

  6. 选择 receiveActivity1,并在**“属性”窗格中的“ServiceOperationInfo”下,单击省略号以打开“选择操作”**对话框。

  7. 选择**“StartupService”并单击“确定”**。

  8. 在**“属性”窗格中的“CanCreateInstance”**下,从下拉菜单中选择 True。这意味着在调用 StartupService 操作时将创建工作流的实例。此属性的默认值设置为 False,如果在调用操作时尚未在客户端和服务之间建立上下文 ID,客户端将收到以下消息:

    There is no context attached to incoming message for the service and the current operation is not marked with "CanCreateInstance = true". In order to communicate with this service check whether the incoming binding supports the context protocol and has a valid context initialized.
    
  9. 在 Workflow1InitialState 设计视图中,选择 setStateActivity1。

  10. 在**“属性”窗格中,将“TargetStateName”**属性的名称从 stateActivity1 更改为 OperationsState。

    重新导航回主工作流设计器视图。现在,在 Workflow1InitialState 和 OperationsState 之间应当有一个连接。

  11. 将五个 EventDrivenActivity 活动拖放到 OperationsState StateActivity 活动中并将它们分别命名为 WaitToAdd、WaitToSubtract、WaitToMultiply、WaitToDivide 和 WaitToEndService。

  12. 双击“WaitToAdd”将其展开。

  13. ReceiveActivity 活动拖放到 WaitToAdd 中。

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

  15. 选择**“添加”,然后单击“确定”**。

  16. CodeActivity 活动拖放到 ReceiveActivity 活动内部。

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

  18. 双击 ExecuteCode 事件的文本框,以便为 ExecuteCode 事件自动生成事件处理程序方法。

  19. 打开 Workflow1.cs(如果您创建的是 Visual Basic 解决方案,则为 Workflow1.vb),将类重命名为 ServerWorkflow,并将模板所创建的 returnValue 和 inputValue 的变量声明更改为:

    Public class ServerWorkflow
        Inherits StateMachineWorkflowActivity
    
        Public returnValue As Int32    Public inputValue As Int32
    
        Private Sub codeActivity1_ExecuteCode(ByVal sender As System.Object, ByVal e As System.EventArgs)
    
        End Sub
    End Class
    
    public sealed partial class ServerWorkflow : StateMachineWorkflowActivity
    {
        public ServerWorkflow()
        {
            InitializeComponent();
        }
    
        public int returnValue = default(int);public int inputValue = default(int);
    
        private void codeActivity1_ExecuteCode(object sender, EventArgs e)
        {
    
        }
    }
    
  20. 导航到 ExecuteCode 的事件处理程序。

    在该事件处理程序体中,赋予 returnValue 如下值:

        Private Sub codeActivity1_ExecuteCode(ByVal sender As System.Object, ByVal e As System.EventArgs)
            returnValue += inputValue
        End Sub
    
        private void codeActivity1_ExecuteCode(object sender, EventArgs e)
        {
            returnValue += inputValue;
        }
    
  21. 返回到 OperationsState 设计器视图,单击 Add ReceiveActivity 活动,在**“属性”**窗格中将 n1(ReturnValue) 参数绑定到 inputValue 和 returnValue,方法是分别突出显示每个参数并单击省略号。此操作将打开一个属性绑定对话框,您可以从中选择要将参数绑定到的相应成员属性。

    另外,在**“属性”窗格中,将“CanCreateInstance”**设置为 False,以便仍使用在步骤 8 中创建的同一工作流实例。否则,如果将 CanCreateInstance 设置为 True,则会为每个操作调用创建一个新的工作流实例。

  22. 从 WaitToSubtract 开始,为每个剩下的 EventDrivenActivity 活动添加 ReceiveActivityCodeActivity 活动。另外,请记住用全局变量 inputValue 和 returnValue 来绑定操作参数。

    完成后,每个数学运算都应当有一个关联的 ReceiveActivity,并且所有 CodeActivity 活动事件处理程序都应按如下方式实现:

        Private Sub codeActivity1_ExecuteCode(ByVal sender As System.Object, ByVal e As System.EventArgs)
            returnValue += inputValue
        End Sub
    
        Private Sub codeActivity2_ExecuteCode(ByVal sender As System.Object, ByVal e As System.EventArgs)
            returnValue -= inputValue
        End Sub
    
        Private Sub codeActivity3_ExecuteCode(ByVal sender As System.Object, ByVal e As System.EventArgs)
            returnValue *= inputValue
        End Sub
    
        Private Sub codeActivity4_ExecuteCode(ByVal sender As System.Object, ByVal e As System.EventArgs)
            returnValue /= inputValue
        End Sub
    
        private void codeActivity1_ExecuteCode(object sender, EventArgs e)
        {
            returnValue += inputValue;
        }
    
        private void codeActivity2_ExecuteCode(object sender, EventArgs e)
        {
            returnValue -= inputValue;
        }
    
        private void codeActivity3_ExecuteCode(object sender, EventArgs e)
        {
            returnValue *= inputValue;
        }
    
        private void codeActivity4_ExecuteCode(object sender, EventArgs e)
        {
            returnValue /= inputValue;
        }
    
  23. 对于 WaitToEndService EventDrivenActivity 活动,请将 ReceiveActivity 活动拖放到 WaitToEndService 内部。

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

  25. 选择**“ShutdownService”并单击“确定”**。

  26. 在 OperationsState 设计视图中,将 SetStateActivity 拖放到 ReceiveActivity 活动下面的 WaitToEndService 内部。

  27. 选择 SetStateActivity 活动,然后在“属性”窗格中的**“TargetStateName”**下,从下拉列表中选择“FinalState”。

保存工作流服务

  1. 打开 App.config 文件。

  2. 在行为元素中添加对 SqlWorkflowPersistenceService 的引用,如下所示。

    <behavior name="WorkflowServiceTutorial.ServerWorkflowBehavior"  >
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="true" />
        <serviceCredentials>
        <windowsAuthentication
            allowAnonymousLogons="false"
            includeWindowsGroups="true" />
        </serviceCredentials>
              <workflowRuntime name="WorkflowServiceHostRuntime" validateOnCreate="true" enablePerformanceCounters="true">
                <services>
                  <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                       connectionString="Data Source=localhost\sqlexpress;Initial Catalog=NetFx35Samples_ServiceWorkflowStore;Integrated Security=True;Pooling=False"
                       LoadIntervalSeconds="1" UnLoadOnIdle= "true" />
                </services>
              </workflowRuntime>
    </behavior>
    

    本教程中使用的 SqlWorkflowPersistenceService 是同一类型的持久性服务,它可以用在仅工作流应用场景中,这意味着只要工作流实例空闲,工作流服务状态信息就会保存到 SQL 数据库中。默认情况下,.NET Framework 3.5 版支持此方案,但是,您可以从 WorkflowPersistenceService 派生此方案,以便将数据保存到其他数据库存储中。

    此外,因为工作流服务可以使用 Windows Workflow Foundation 的持久性功能,所以可以在操作实现的执行期间保持工作流服务状态信息,而不必像在持久性服务中那样在完成后再保存。

    PersistenceProviderElement 中的 connectionString 属性值设置为“WorkflowServiceStore”。这是在尝试连接到 SQL 数据库时将使用的连接字符串的名称。

    提示

    本教程使用的是 NetFx35Samples_ServicesWorkflowStore 数据库。若要创建该数据库,请从 One-Time Setup Procedure for the Windows Communication Foundation Samples中运行 Createstores.cmd 脚本。

  3. 生成并运行服务。WcfSvcHost.exe 将开始运行,并承载您的服务。WcfSvcHost.exe 是一种承载服务的开发人员工具,以供测试使用。为了实现测试目的,**“顺序工作流服务库”“状态机工作流服务库”**模板会自动使用 WcfSvcHost.exe 应用程序来承载您的工作流服务,这样您不必亲自创建主机应用程序。

注释

在完成工作流服务的创建之后,可以按照任务 2:创建工作流服务客户端中的说明来创建一个能够与该工作流服务交互的工作流服务客户端。

另请参见

任务

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

概念

如何:配置工作流服务

其他资源

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

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