使事务流入和流出工作流服务

工作流服务和客户端可以参与事务。 对于将成为环境事务一部分的服务操作,将 Receive 活动放到 TransactedReceiveScope 活动内。 由 Send 内的 SendReplyTransactedReceiveScope 活动所做的任何调用也将在环境事务中进行。 工作流客户端应用程序可以通过使用 TransactionScope 活动来创建环境事务,并通过使用该环境事务来调用服务操作。 本主题将指导您创建参与事务的工作流服务和工作流客户端。

警告

如果某个工作流服务实例在事务中加载且该工作流包含 Persist 活动,则该工作流实例会阻塞,直到事务超时。

重要

无论何时使用 TransactedReceiveScope,都建议将所有接收都置于工作流内的 TransactedReceiveScope 活动中。

重要

如果 TransactedReceiveScope 且消息按错误顺序到达,则在尝试提交第一个不按顺序的消息时,工作流会中止。 必须确保工作流在空闲时始终处于一致的停止点。 这会使您可以在工作流中止时,从以前的持久点重新启动工作流。

创建共享库

  1. 创建新的空 Visual Studio 解决方案。

  2. 添加一个名为 Common 的新类库项目。 添加对下列程序集的引用:

    • System.Activities.dll

    • System.ServiceModel.dll

    • System.ServiceModel.Activities.dll

    • System.Transactions.dll

  3. 将一个名为 PrintTransactionInfo 的新类添加到 Common 项目。 此类派生自 NativeActivity,并重载 Execute 方法。

    using System;  
    using System;  
    using System.Activities;  
    using System.Transactions;  
    
    namespace Common  
    {  
        public class PrintTransactionInfo : NativeActivity  
        {  
            protected override void Execute(NativeActivityContext context)  
            {  
                RuntimeTransactionHandle rth = context.Properties.Find(typeof(RuntimeTransactionHandle).FullName) as RuntimeTransactionHandle;  
    
                if (rth == null)  
                {  
                    Console.WriteLine("There is no ambient RuntimeTransactionHandle");  
                }  
    
                Transaction t = rth.GetCurrentTransaction(context);  
    
                if (t == null)  
                {  
                    Console.WriteLine("There is no ambient transaction");  
                }  
                else  
                {  
                    Console.WriteLine("Transaction: {0} is {1}", t.TransactionInformation.DistributedIdentifier, t.TransactionInformation.Status);  
                }  
            }  
        }  
    
    }  
    

    这是一个本机活动,用于显示有关环境事务的信息,它将在本主题中使用的服务工作流和客户端工作流中使用。 生成该解决方案以使此活动在“工具箱”的“通用”部分中可用。

实现工作流服务

  1. 将一个名为 WorkflowService 的新 WCF 工作流服务添加到 Common 项目。 为此,右击 Common 项目,依次选择“添加”、“新建项...”,然后在“已安装的模板”下选择“工作流”,再选择“WCF 工作流服务”。

    Adding a Workflow Service

  2. 删除默认的 ReceiveRequestSendResponse 活动。

  3. WriteLine 活动拖放到 Sequential Service 活动中。 将文本属性设置为 "Workflow Service starting ...",如下面的示例所示。

    ![将 WriteLine 活动添加到顺序服务活动(./media/flowing-transactions-into-and-out-of-workflow-services/add-writeline-sequential-service.jpg)

  4. TransactedReceiveScope 拖放到 WriteLine 活动后面。 TransactedReceiveScope 活动可以在“工具箱”的“消息传递”部分中找到。 TransactedReceiveScope 活动由“请求”和“正文”两部分组成。 “请求”部分包含 Receive 活动。 “正文”部分包含收到消息后将在事务中执行的活动。

    Adding a TransactedReceiveScope activity

  5. 选择 TransactedReceiveScope 活动,然后单击“变量”按钮。 添加以下变量。

    Adding variables to the TransactedReceiveScope

    注意

    可以删除默认情况下在该处的数据变量。 还可以使用现有句柄变量。

  6. Receive 活动拖放到 TransactedReceiveScope 活动的“请求”部分内。 设置以下属性:

    属性
    CanCreateInstance True(选中复选框)
    OperationName StartSample
    ServiceContractName ITransactionSample

    该工作流应如下所示:

    Adding a Receive activity

  7. 单击 Receive 活动中的“定义...”链接,然后进行以下设置:

    Setting message settings for the Receive activity

  8. Sequence 活动拖放到 TransactedReceiveScope 的“正文”部分内。 在 Sequence 活动内,拖放两个 WriteLine 活动并设置 Text 属性,如下表所示。

    活动
    第一个 WriteLine "Service: Receive Completed"
    第二个 WriteLine "Service: Received = " + requestMessage

    现在,该工作流应如下所示:

    Sequence after adding WriteLine activities

  9. 将第二个 WriteLine 活动后面的 PrintTransactionInfo 活动拖放到 TransactedReceiveScope 活动的“正文”中。

    Sequence after adding PrintTransactionInfo

  10. Assign 活动拖放到 PrintTransactionInfo 活动后面,然后根据下表设置其属性。

    属性
    功能 replyMessage
    "Service: Sending reply."
  11. WriteLine 活动拖放到 Assign 活动后面,然后将它的 Text 属性设置为 "Service: Begin reply."

    现在,该工作流应如下所示:

    After adding Assign and WriteLine

  12. 右击 Receive 活动并选择“创建 SendReply”,然后将其粘贴到最后一个 WriteLine 活动后面。 单击 SendReplyToReceive 活动中的“定义...”链接,然后进行以下设置。

    Reply message settings

  13. WriteLine 活动拖放到 SendReplyToReceive 活动后面,然后将它的 Text 属性设置为 "Service: Reply sent"。

  14. WriteLine 活动拖放到工作流底部,然后将它的 Text 属性设置为 "Service: Workflow ends, press ENTER to exit."

    完成的服务工作流应如下所示:

    Complete Service Workflow

实现工作流客户端

  1. 将一个名为 WorkflowClient 的新 WCF 工作流应用程序添加到 Common 项目。 为此,右击 Common 项目,依次选择“添加”、“新建项...”,然后在“已安装的模板”下选择“工作流”,再选择“活动”。

    Add an Activity project

  2. Sequence 活动拖放到设计图面上。

  3. Sequence 活动内拖放一个 WriteLine 活动,然后将它的 Text 属性设置为 "Client: Workflow starting"。 现在,该工作流应如下所示:

    Add a WriteLine activity

  4. TransactionScope 活动拖放到 WriteLine 活动后面。 选择 TransactionScope 活动,单击“变量”按钮,然后添加以下变量。

    Add variables to the TransactionScope

  5. Sequence 活动拖放到 TransactionScope 活动的正文内。

  6. PrintTransactionInfo 内拖放 Sequence 活动

  7. WriteLine 活动拖放到 PrintTransactionInfo 活动后面,然后将它的 Text 属性设置为 “Client: Beginning Send”。 现在,该工作流应如下所示:

    Adding Client: Beginning Send activities

  8. Send 活动拖放到 Assign 活动后面,并设置以下属性:

    属性
    EndpointConfigurationName workflowServiceEndpoint
    OperationName StartSample
    ServiceContractName ITransactionSample

    现在,该工作流应如下所示:

    Setting the Send activity properties

  9. 单击“定义...”链接,然后进行以下设置:

    Send activity message settings

  10. 右击 Send 活动,然后选择“创建 ReceiveReply”。 ReceiveReply 活动将自动放在 Send 活动后面。

  11. 单击 ReceiveReplyForSend 活动上的“定义...”链接,然后进行以下设置:

    Setting the ReceiveForSend message settings

  12. WriteLine 活动拖放到 SendReceiveReply 活动之间,然后将它的 Text 属性设置为 "Client: Send complete."

  13. WriteLine 活动拖放到 ReceiveReply 活动后面,然后将它的 Text 属性设置为 "Client side: Reply received = " + replyMessage

  14. PrintTransactionInfo 活动拖放到 WriteLine 活动后面。

  15. WriteLine 活动拖放到工作流末尾,然后将它的 Text 属性设置为 "Client workflow ends"。完成的客户端工作流应如下图所示。

    The completed client workflow

  16. 生成解决方案。

创建服务应用程序

  1. 将一个名为 Service 的新控制台应用程序项目添加到该解决方案。 添加对下列程序集的引用:

    1. System.Activities.dll

    2. System.ServiceModel.dll

    3. System.ServiceModel.Activities.dll

  2. 打开生成的 Program.cs 文件并添加以下代码:

          static void Main()  
          {  
              Console.WriteLine("Building the server.");  
              using (WorkflowServiceHost host = new WorkflowServiceHost(new DeclarativeServiceWorkflow(), new Uri("net.tcp://localhost:8000/TransactedReceiveService/Declarative")))  
              {
                  //Start the server  
                  host.Open();  
                  Console.WriteLine("Service started.");  
    
                  Console.WriteLine();  
                  Console.ReadLine();  
                  //Shutdown  
                  host.Close();  
              };
          }  
    
  3. 将以下 app.config 文件添加到项目。

    <?xml version="1.0" encoding="utf-8" ?>  
    <!-- Copyright © Microsoft Corporation.  All rights reserved. -->  
    <configuration>  
        <system.serviceModel>  
            <bindings>  
                <netTcpBinding>  
                    <binding transactionFlow="true" />  
                </netTcpBinding>  
            </bindings>  
        </system.serviceModel>  
    </configuration>  
    

创建客户端应用程序

  1. 将一个名为 Client 的新控制台应用程序项目添加到该解决方案。 添加对 System.Activities.dll 的引用。

  2. 打开 program.cs 文件并添加以下代码。

    class Program  
    {  
    
        private static AutoResetEvent syncEvent = new AutoResetEvent(false);  
    
        static void Main(string[] args)  
        {  
            //Build client  
            Console.WriteLine("Building the client.");  
            WorkflowApplication client = new WorkflowApplication(new DeclarativeClientWorkflow());  
            client.Completed = Program.Completed;  
            client.Aborted = Program.Aborted;  
            client.OnUnhandledException = Program.OnUnhandledException;  
            //Wait for service to start  
            Console.WriteLine("Press ENTER once service is started.");  
            Console.ReadLine();  
    
            //Start the client
            Console.WriteLine("Starting the client.");  
            client.Run();  
            syncEvent.WaitOne();  
    
            //Sample complete  
            Console.WriteLine();  
            Console.WriteLine("Client complete. Press ENTER to exit.");  
            Console.ReadLine();  
        }  
    
        private static void Completed(WorkflowApplicationCompletedEventArgs e)  
        {  
            Program.syncEvent.Set();  
        }  
    
        private static void Aborted(WorkflowApplicationAbortedEventArgs e)  
        {  
            Console.WriteLine("Client Aborted: {0}", e.Reason);  
            Program.syncEvent.Set();  
        }  
    
        private static UnhandledExceptionAction OnUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e)  
        {  
            Console.WriteLine("Client had an unhandled exception: {0}", e.UnhandledException);  
            return UnhandledExceptionAction.Cancel;  
        }  
    }  
    

另请参阅