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

工作流服务和客户端可以参与事务。 为了使服务操作成为环境事务的一部分,请将 Receive 活动置于 TransactedReceiveScope 活动之内。 由 Send 内的 SendReplyTransactedReceiveScope 活动所做的任何调用也将在环境事务中进行。 工作流客户端应用程序可以使用 TransactionScope 活动创建背景事务,并使用背景事务调用服务操作。 本主题指导你创建参与事务的工作流服务和工作流客户端。

警告

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

重要

每当使用 TransactedReceiveScope 时,建议将工作流中的所有 Receives 置于 TransactedReceiveScope 活动内。

重要

使用TransactedReceiveScope 时,如果消息以不正确的顺序到达,在尝试传递第一条无序消息时,工作流将被中止。 必须确保工作流在空闲时始终处于一致的停止点。 这样,如果工作流中止,则可以从以前的暂留点重启工作流。

创建共享库

  1. 创建一个全新的空白 Visual Studio 解决方案。

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

    • System.Activities.dll

    • System.ServiceModel.dll

    • System.ServiceModel.Activities.dll

    • System.Transactions.dll

  3. 向项目添加新类PrintTransactionInfoCommon。 此类派生自 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项目中添加一个名为Common的新WCF工作流服务。 若要执行此作,请单击Common项目,选择“添加”、“新建项目...”,在“已安装的模板”下选择“工作流”,然后选择 WCF 工作流服务

    添加工作流服务

  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 活动。 “正文”部分包含收到消息后将在事务中执行的活动

    添加 TransactedReceiveScope 活动

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

    向 TransactedReceiveScope 添加变量

    注释

    可以删除默认存在的数据变量。 还可以使用现有句柄变量。

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

    资产 价值
    CanCreateInstance True (选中复选框)
    操作名称 StartSample
    服务合同名称 ITransactionSample

    工作流应如下所示:

    添加 Receive 活动

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

    为 Recieve 活动设置消息设置

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

    活动 价值
    第一个 WriteLine “服务:接收已完成”
    第二个 WriteLine “Service: Received = ” + requestMessage

    工作流现在应如下所示:

    添加 WriteLine 活动后的顺序

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

    添加 PrintTransactionInfo 后的顺序

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

    资产 价值
    replyMessage
    价值 “服务:发送回复。”
  11. WriteLine 活动拖放到 Assign 活动后面,然后将它的 Text 属性设置为 "Service: Begin reply."

    工作流现在应如下所示:

    添加 Assign 和 WriteLine 后

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

    回复消息设置

  13. WriteLine活动拖放到SendReplyToReceive活动之后,并将其Text属性设置为“服务:已发送答复”。

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

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

    完成服务工作流

实现工作流客户端

  1. WorkflowClient项目添加一个名为Common的新WCF工作流应用程序。 若要执行此作,请单击Common项目,选择“添加”、“新建项...”,在“已安装的模板”下选择“工作流”,然后选择“活动”。

    添加活动项目

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

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

    添加 WriteLine 活动

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

    将变量添加到 TransactionScope

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

  6. PrintTransactionInfo 内拖放 Sequence 活动

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

    添加客户端:开始发送活动

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

    资产 价值
    终端配置名称 workflowServiceEndpoint
    操作名称 StartSample
    服务合同名称 ITransactionSample

    工作流现在应如下所示:

    设置发送活动属性

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

    发送活动消息设置

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

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

    设置 ReceiveForSend 消息的设置

  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"。完成的客户端工作流应如下图所示。

    已完成的客户端工作流

  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;  
        }  
    }  
    

另请参阅