附录 A:开发订单应用程序

此附录为开发人员提供了构建 Contoso 订单服务所需的详细步骤。此附录的目的是使开发人员熟悉如何通过使用 Visual Studio 2010 和 .NET Framework 4 开发应用程序(包括 Windows Communication Foundation (WCF) 和/或 Windows Workflow Foundation (WF) 服务)。本教程的主要部分使用此应用程序向系统管理员或应用程序所有者介绍如何使用 Windows Server AppFabric 来部署、监控应用程序(包括 WCF 和/或 WF 服务)并对其进行疑难解答。

此 Contoso 订单服务由以下四个应用程序组成:

  • 订单处理服务: 一种模拟通过 Web 服务界面调用现有订单处理应用程序的 WCF 服务。

  • 发售服务: 一种模拟通过 API 调用现有发售应用程序的 WCF 服务。

  • 订单工作流服务: 一种用于管理订单流程(包括接收订单、处理订单和发售订单)的 WF 工作流服务。

  • 订单客户端: 一种用作订单服务前端的 Windows 窗体应用程序。

备注

该附录不要求您安装 Windows Server AppFabric。但是您必须首先安装 Windows Server AppFabric 界面的使用教程文件以创建正确的文件结构,该附录才能正确生成。请注意,该附录不要求您首先完成 Windows Server AppFabric 界面的使用教程教程 - 只是安装这些文件。有关 Windows Server AppFabric 界面的使用教程教程的安装过程,请参阅第 1 课:入门C:\DublinTutorial\OrderServiceSolution\Completed 文件夹中包含已完成解决方案的副本。

过程

您将通过完成以下步骤来创建该应用程序:

  1. 开发订单处理 WCF 服务

  2. 开发发售 WCF 服务

  3. 开发订单工作流 WF 服务

  4. 完成订单处理 WCF 服务

  5. 完成 WCF 服务的发售

  6. 开发订单客户端应用程序

  7. 将订单服务打包

开发订单处理 WCF 服务

订单处理应用程序是一个 Contoso 购买的应用程序。它提供了其他应用程序可以与之通信的 Web 服务。作为一名 Contoso 开发人员,您需要开发一个名为 OrderProcessingService 的 WCF 服务,以与订单处理应用程序交互。

为 OrderProcessingService 创建 Visual Studio 解决方案和 WCF 服务应用程序的步骤

  1. 单击「开始」,依次指向“所有程序”、“Microsoft Visual Studio 2010”,然后单击“Microsoft Visual Studio 2010”。

  2. 从“文件”菜单中,单击“新建”,然后单击“新建项目”。

  3. 从“新建项目”中,选择或键入以下值,然后单击“确定”。

    属性

    项目类型

    Visual C#/Web

    模板

    WCF 服务应用程序

    名称

    OrderProcessingService

    位置

    C:\DublinTutorial\OrderServiceSolution

    解决方案名称

    OrderService

    为解决方案创建目录

    (已选择)

  4. 在“解决方案资源管理器”中,展开“OrderProcessingService”,右键单击“IService1.cs”,然后单击“删除”。

  5. 单击“确定”确认永久删除此文件。

  6. 在“解决方案资源管理器”中,展开“OrderProcessingService”,右键单击“Service1.svc”,然后单击“删除”。

  7. 单击“确定”确认永久删除此文件。

创建 WCF 服务的第一个任务是定义协定。协定中指定服务支持的操作。操作可以看作是 Web 服务方法。接口中的每个方法都对应一个特定的服务操作。在 OrderProcessingService 中,您将定义以下两个方法:ProcessOrder 和 CancelOrderProcess。

为订单处理服务定义服务协定和数据协定的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderProcessService”,指向“添加”,然后单击“新项目”。

  2. 从“添加新项目 - OrderProcessService”中,选择或键入以下值,然后单击“添加”。

    属性

    类别

    Visual C#/Web

    模板

    WCF 服务

    名称

    OrderProcessing.svc

    已将以下两个文件添加到此解决方案:IOrderProcessing.cs 和 OrderProcessing.svc。

  3. 在“解决方案资源管理器”中,双击“IOrderProcessing.cs”将其打开。

  4. 右键单击“OrderProcessingService”命名空间,单击“重构”,然后单击“重命名”打开“重命名”对话框。

  5. 在“新名称”中,键入“Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService”,然后单击“确定”。

  6. 单击“应用”,然后单击“是”。

  7. 修改 OrderProcessing.svc 源代码,使它看起来类似于如下所示:

    using System; 
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    
    namespace Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService
    {
        [ServiceContract]
        public interface IOrderProcessing
        {
            [OperationContract]
            string ProcessOrder(PurchaseOrder po);
    
            [OperationContract]
            string CancelOrderProcess(string orderID);
        }
    
        [DataContract]
        public class PurchaseOrder
        {
            [DataMember]
            public string POID;
            [DataMember]
            public string FirstName;
            [DataMember]
            public string LastName;
            [DataMember]
            public string EmailAddress;
            [DataMember]
            public string TelephoneNumber;
            [DataMember]
            public string AddressLine1;
            [DataMember]
            public string AddressLine2;
            [DataMember]
            public string City;
            [DataMember]
            public string State;
            [DataMember]
            public string ZipCode;
            [DataMember]
            public string Description;
            [DataMember]
            public int Quantity;
            [DataMember]
            public string UnitPrice;
        }
    }
    

    在此文件中,您将定义数据协定和服务协定。客户端可以调用服务来处理订单及取消订单处理。

在您创建协定(通过使用接口定义)之后,下一步是实现该接口。此操作包括创建名为 OrderProcessService 的类,用于实现用户定义的 IOrderProcessing 接口。

实现订单处理服务协定的步骤

  1. 在“解决方案资源管理器”中,双击“IOrderProcessing.cs”将其打开。

  2. 修改源代码,使它看起来类似于如下所示:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    using System.Threading;
    using System.IO;
    
    namespace Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService
    {
        class WorkItem
        {
            private Thread workThread;
            private object workItemLock;
            private bool completeFlag;
    
            public Thread WorkThread { get { return workThread; } set { workThread = value; } }
            public object WorkItemLock { get { return workItemLock; } }
            public bool CompleteFlag { get { return completeFlag; } }
    
            public WorkItem(Thread WorkThread)
            {
                workThread = WorkThread;
                workItemLock = new object();
                completeFlag = false;
            }
    
            public void Complete()
            {
                completeFlag = true;
            }
        }
    
        public class OrderProcessing : IOrderProcessing
        {
            private static Dictionary<String, WorkItem> WorkItemMap = new Dictionary<String, WorkItem>();
    
            public string ProcessOrder(PurchaseOrder po)
            {
                //run the code from a different thread to simulate asynchronized call
                ThreadPool.QueueUserWorkItem(SendProcessResult, po);
                return ("The request for processing order[" + po.POID + "] has been received.");
            }
    
            private void SendProcessResult(object state)
            {
                PurchaseOrder po = (PurchaseOrder)state;
    
                WorkItem workItem = new WorkItem(Thread.CurrentThread);
                WorkItemMap.Add(po.POID, workItem);
    
                //Simulating the order processing process
                Thread.Sleep(120000);
    
                //The following code will be uncommented later in the process
                //OrderWorkflowService.ProcessServiceResult reply = new OrderWorkflowService.ProcessServiceResult();
                //reply.POID = po.POID;
                //reply.Message = "The order has been processed successfully.";
    
                //lock (workItem.WorkItemLock)
                //{
                //    using (OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient client = new OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient())
                //    {
                //        client.SubmitProcessResult(reply);
                //    }
    
                //    workItem.Complete();
                //    WorkItemMap.Remove(po.POID);
                //}
    
            }
    
            public string CancelOrderProcess(string poID)
            {
                string ret = "Cancel unavailable for this order.";
    
                //=====================================================//
                //===[ Attempt to get a work item for the order Id 
                //=====================================================//
                WorkItem workItem;
                if (WorkItemMap.TryGetValue(poID, out workItem) == true)
                {
                    //===========================================================
                    //=== Slight race condition here. Workitem could complete
                    //=== before we aquire its lock. So we check the          
                    //=== completion flag inside the lock.                    
                    //===========================================================
                    lock (workItem.WorkItemLock)
                    {
                        if ((!workItem.CompleteFlag) && (workItem.WorkThread.IsAlive))
                        {
                            workItem.WorkThread.Abort();
                            WorkItemMap.Remove(poID);
                            ret = "The order process has been terminated successfully.";
                        }
                    }
                }
                return ret;
            }
        }
    }
    

使用配置文件可以使您在部署时(而不是设计时)灵活地提供终结点和服务行为数据。在此配置文件中,您将定义两个终结点。

使用配置文件配置订单处理服务的步骤

  1. 在“解决方案资源管理器”中,展开“OrderProcessingService”,然后双击“Web.config”将其打开。在 <system.serviceModel> 标记中添加 <services> 标记:

        <services>
          <service name="Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.OrderProcessing">
            <endpoint address="" binding="basicHttpBinding" contract="Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.IOrderProcessing" />
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
          </service>
        </services>
    

编译订单处理 WCF 服务的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderProcessingService”项目,然后单击“重建”。确保此项目在“输出”窗口中成功编译。

测试订单处理 WCF 服务的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderProcessingService”项目,然后单击“在浏览器中查看”。将打开一个 Internet Explorer 窗口,其中列出目录文件。

  2. 从 Internet Explorer 窗口中,单击“OrderProcessing.svc”。确保您未遇到任何错误。

开发发售 WCF 服务

发售服务是一种调用 SQL Server 存储的 WCF 服务。在此模拟中,您并不真正地调用数据库。

向此解决方案中添加新 WCF 服务应用程序项目的步骤

  1. 在“解决方案资源管理器”中,右键单击“解决方案‘OrderService’”,指向“添加”,然后单击“新项目”。

  2. 在“添加新项目”中,选择或键入以下值,然后单击“确定”。

    属性

    项目类型

    Visual C#/Web

    模板

    WCF 服务应用程序

    名称

    ShippingService

    位置

    C:\DublinTutorial\OrderServiceSolution\OrderService

  3. 在“解决方案资源管理器”中,展开“ShippingService”,右键单击“IService1.cs”,然后单击“删除”。

  4. 单击“确定”确认永久删除此文件。

  5. 在“解决方案资源管理器”中,展开“ShippingService”,右键单击“Service1.svc”,然后单击“删除”。

  6. 单击“确定”确认永久删除此文件。

您将定义一个名为 IShipping 的服务协定,其中包含两个操作协定,即 ShipOrder 和 CancelShipping。

定义发售 WCF 服务协定的步骤

  1. 在“解决方案资源管理器”中,右键单击“ShippingService”,指向“添加”,然后单击“新项目”。

  2. 在“添加新项目 - ShippingService”中,选择或键入以下值,然后单击“添加”。

    属性

    类别

    Visual C#/Web

    模板

    WCF 服务

    名称

    Shipping.svc

    会将以下两个文件添加到项目中:IShipping.cs 和 Shipping.svc。

  3. 在“解决方案资源管理器”中,双击“IShipping.cs”将其打开。

  4. 右键单击“ShippingService”命名空间,单击“重构”,然后单击“重命名”以打开“重命名”对话框。

  5. 在“新名称”中,键入“Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService”,然后单击“确定”。

  6. 单击“应用”,然后单击“是”。

  7. 修改源代码,使它看起来类似于如下所示:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    
    namespace Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService
    {
        [ServiceContract]
        public interface IShipping
        {
            [OperationContract]
            string ShipOrder(string poID);
    
            [OperationContract]
            string CancelShipping(string poID);
        }
    }
    

实现发售 WCF 服务协定的步骤

  1. 在“解决方案资源管理器”中,双击“Shipping.svc”将其打开。

  2. 修改源代码,使它看起来类似于如下所示:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    using System.Threading;
    
    namespace Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService
    {
        class WorkItem
        {
            private Thread workThread;
            private object workItemLock;
            private bool completeFlag;
    
            public Thread WorkThread { get { return workThread; } set { workThread = value; } }
            public object WorkItemLock { get { return workItemLock; } }
            public bool CompleteFlag { get { return completeFlag; } }
    
            public WorkItem(Thread WorkThread)
            {
                workThread = WorkThread;
                workItemLock = new object();
                completeFlag = false;
            }
    
            public void Complete()
            {
                completeFlag = true;
            }
        }
    
        public class Shipping : IShipping
        {
            private static Dictionary<String, WorkItem> WorkItemMap = new Dictionary<String, WorkItem>();
    
            public string ShipOrder(string poID)
            {
                //run the code from a different thread to simulate asynchronized call
                ThreadPool.QueueUserWorkItem(SendShippingResult, poID);
                return ("The request for processing order[" + poID + "] has been received.");
            }
    
            private void SendShippingResult(object state)
            {
                string poID = state.ToString();
    
                WorkItem workItem = new WorkItem(Thread.CurrentThread);
                WorkItemMap.Add(poID, workItem);
    
                //Simulating the order processing process
                Thread.Sleep(60000);
    
                //The following portion will be uncommented after referencing OrderWorkflowService
                //OrderWorkflowService.ShippingServiceResult reply = new OrderWorkflowService.ShippingServiceResult();
                //reply.POID = poID;
                //reply.Message = "The order has been shipped.";
    
                //lock (workItem.WorkItemLock)
                //{
                //    using (OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient client = new OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient())
                //    {
                //        client.SubmitShippingResult(reply);
                //    }
    
                //    workItem.Complete();
                //    WorkItemMap.Remove(poID);
                //}
            }
    
            public string CancelShipping(string poID)
            {
                string ret = "Cancel unavailable for this order.";
    
                //=====================================================//
                //===[ Attempt to get a work item for the order Id 
                //=====================================================//
                WorkItem workItem;
                if (WorkItemMap.TryGetValue(poID, out workItem) == true)
                {
                    //===========================================================
                    //=== Slight race condition here. Workitem could complete
                    //=== before we aquire its lock. So we check the          
                    //=== completion flag inside the lock.                    
                    //===========================================================
                    lock (workItem.WorkItemLock)
                    {
                        if ((!workItem.CompleteFlag) && (workItem.WorkThread.IsAlive))
                        {
                            workItem.WorkThread.Abort();
                            WorkItemMap.Remove(poID);
                            ret = "The shipping process has been terminated successfully.";
                        }
                    }
                }
                return ret;
            }
        }
    }
    

在此配置文件中,您将定义两个终结点。

使用配置文件配置发售 WCF 服务的步骤

  1. 在“解决方案资源管理器”中,展开“ShippingService”,然后双击“Web.config”将其打开。在 <system.serviceModel> 标记中添加 <services> 标记:

        <services>
          <service name="Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.Shipping">
            <endpoint address="" binding="basicHttpBinding" contract="Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.IShipping" />
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
          </service>
        </services>
    

编译发售 WCF 服务的步骤

  1. 在“解决方案资源管理器”中,右键单击“ShippingService”项目,然后单击“重建”。确保此项目在“输出”窗口中成功编译。

开发订单工作流 WF 服务

订单工作流服务应用程序是整个服务的主要部分。它用于安排整个业务流程。它接收采购订单,然后调用 OrderProcessingService 和 ShippingService,并最终将关于采购订单状态的电子邮件发送给客户。

向解决方案中添加新 WCF 工作流服务应用程序项目的步骤

  1. 在“解决方案资源管理器”中,右键单击“解决方案‘OrderService’”,指向“添加”,然后单击“新项目”。

  2. 在“添加新项目”中,选择或键入以下值,然后单击“确定”。

    属性

    项目类型

    Visual C#/工作流

    模板

    WCF 工作流服务应用程序

    名称

    OrderWorkflowService

    位置

    C:\DublinTutorial\OrderServiceSolution\OrderService

OrderWorkflowService 使用 OrderProcessingService 和 ShippingService。您必须引用这两个服务。

添加服务引用的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderWorkflowService”,然后单击“添加服务引用”。

  2. 在“添加服务引用”中,单击“发现”。Visual Studio 应会发现这两个服务。

  3. 输入并选择以下值,然后单击“确定”以创建服务引用。

    属性

    服务

    OrderProcessing.svc

    命名空间

    OrderProcessService

  4. 重复这些步骤,以使用以下值来添加其他服务引用:

    属性

    服务

    Shipping

    命名空间

    ShippingService

您需要定义用于模拟发送电子邮件通知的自定义工作流活动。

创建工作流代码活动的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderWorkflowService”,指向“添加”,然后单击“新项目”。

  2. 从“添加新项目 - OrderWorkflowService”中,选择或键入以下值,然后单击“添加”。

    属性

    类别

    Visual C#/工作流

    模板

    Code Activity

    名称

    SendNotification.cs

  3. 在“解决方案资源管理器”中,双击“SendNotification.cs”将其打开。

  4. 右键单击“OrderWorkflowService”命名空间,单击“重构”,然后单击“重命名”以打开“重命名”对话框。

  5. 在**“新名称”**中,键入“Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService”,然后单击“确定”。

  6. 单击“应用”,然后单击“是”。

  7. 修改源代码,使它看起来类似于如下所示:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Activities;
    using System.IO;
    
    namespace Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
    {
        public class SendNotification : CodeActivity
        {
            InArgument<string> to;
            InArgument<string> subject;
            InArgument<string> body;
            string pathRoot = @"C:\DublinTutorial\Inbox\";
    
            public InArgument<string> To { get { return this.to; } set { this.to = value; } }
            public InArgument<string> Subject { get { return this.subject; } set { this.subject = value; } }
            public InArgument<string> Body { get { return this.body; } set { this.body = value; } }
            public SendNotification() { }
    
            public SendNotification(InArgument<string> To, InArgument<string> Subject, InArgument<string> Body)
            {
                this.to = To; this.subject = Subject; this.body = Body;
            }
    
            protected override void Execute(CodeActivityContext context)
            {
                string filename;
                string content;
    
                try
                {
                    filename = this.to.Get<String>(context) + "~~" + this.subject.Get<string>(context) + "_" + DateTime.Now.ToFileTime() + ".txt";
                    content = String.Format("To: {0}" + Environment.NewLine
                        + "From: {1}" + Environment.NewLine
                        + "Subject: {2}" + Environment.NewLine
                        + Environment.NewLine
                        + "{3}",
                        this.to.Get<String>(context), "CustomerRelations@Contoso.com", this.subject.Get<String>(context), this.body.Get<String>(context));
    
                    File.WriteAllText((pathRoot + filename), content);
                }
                catch (Exception Ex)
                {
                    context.SetValue(Body, Ex.Message);
                }
    
            }
        }
    }
    

    请注意,此路径被硬编码为“C:\DublinTutorial\Inbox\”。

在以下过程中,您将定义数据类型。这些数据类型用于 OrderProcessingService 和 ShippingService 将结果发送回 OrderWorkflowService。

定义数据类型的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderWorkflowService”,指向“添加”,然后单击“新项目”。

  2. 从“添加新项目 - OrderWorkflowService”中,选择或键入以下值,然后单击“添加”。

    属性

    类别

    Visual C#/代码

    模板

    代码文件

    名称

    DataTypes.cs

  3. 在“解决方案资源管理器”中,双击“DataTypes.cs”将其打开。

  4. 修改源代码,使它看起来类似于如下所示:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    
    namespace Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
    {
        [DataContract]
        public class ProcessServiceResult
        {
            [DataMember]
            public string POID;
            [DataMember]
            public string Message;
        }
    
        [DataContract]
        public class ShippingServiceResult
        {
            [DataMember]
            public string POID;
            [DataMember]
            public string Message;
        }
    }
    
  5. 在“解决方案资源管理器”中,右键单击“OrderWorkflowService”,然后单击“重建”。您必须构建项目,以便能够从您将在后续步骤中开发的工作流访问代码活动。编译还可以将引用的 WCF 服务终结点显示给工作流。

下一步是定义工作流。工作流包含几种状态。您先从定义状态开始,然后再定义状态细节。开发过程的每一部分都可以包含以下步骤:

  1. 使用活动撰写工作流。

  2. 定义变量。

  3. 配置活动。

定义工作流的步骤

  1. 在“解决方案资源管理器”中,展开“OrderWorkflowService”,右键单击“Service1.xamlx”,然后单击“重命名”。将文件重命名为“OrderWorkflow.xamlx”。

  2. 在“解决方案资源管理器”中,双击“OrderWorkflow.xamlx”将其打开。顺序服务中列出了以下两个活动:一个名为 ReceiveRequest,另一个名为 SendResponse。

  3. 右键单击“顺序服务”活动,然后单击“删除”。

  4. 在工作流中,单击“将活动放在此处”,然后在“属性”面板中设置以下值。

    属性

    configurationName

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderWorkflow

    名称

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderWorkflow

  5. 单击窗口左侧的“工具箱”打开“工具箱”面板,然后将其固定在窗口左侧。

  6. 将以下活动从工具箱拖动到显示“将活动放在此处”的工作流中。

    类别 活动 注意

    流程图

    流程图

    默认情况下,流程图活动是包含“开始”的复合活动。您将在以下步骤中向其中添加更多活动。

  7. 将以下活动按显示顺序从工具箱拖动到工作流:

    类别 活动 注意

    消息

    ReceiveAndSendReply

    这是“等待订单”状态。

    控制流

    Sequence

    这将包含第二个状态“订单已打开”。

    流程图

    FlowDecision

    FlowDecision 活动帮助在状态之间转换。

    控制流

    Sequence

    这将包含第三个状态“订单已处理”。

    流程图

    FlowDecision

    FlowDecision 活动帮助在状态之间转换。

    控制流

    Sequence

    这将包含最后一个状态“订单已完成”。

  8. 使用鼠标指针连接活动,使它们看起来类似于如下所示:

    67e7c9dd-77e7-43be-ad5a-797b3b46f6e8

  9. 单击工作流底部的“变量”打开“变量”面板。

  10. 在“变量”面板中,单击“创建变量”,然后创建以下变量:

    变量名 变量类型 作用域 注意

    poID

    字符串

    流程图

    poID 是 GUID 号。poID 也用作相关性。

    isProcessed

    布尔型

    流程图

    它是一个标志,表示订单是否已成功处理。

    isShipped

    布尔型

    流程图

    它是一个标志,表示订单是否已发售。

    isUpdated

    布尔型

    流程图

    它是一个标志,表示订单是否已由客户更新。

    po

    PurchaseOrder

    流程图

    这是一个在 OrderProcessingService 中定义的自定义数据类型。它包含采购订单信息。

    poUpdate

    PurchaseOrder

    流程图

    它包含客户要更新 PO 时已更新的 PO 信息。

    correlationorderWorkflow

    CorrelationHandle

    流程图

    这是相关句柄,用于连接到服务的客户端,以及连接到 OrderProcessService 和 ShippingService 的服务。

    以下是所创建的变量的屏幕截图:

    9208977f-710c-460f-afd8-5c6bd8a792d9

  11. 在工作流中,单击“流程图”活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    订单服务

  12. 在工作流中,单击第一个“序列”活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    等待订单

  13. 在工作流中,单击第二个“序列”活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    订单已打开

  14. 在工作流中,单击第三个“序列”活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    订单已处理

  15. 在工作流中,单击第四个“序列”活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    订单已完成

  16. 在工作流中,单击第一个“FlowDecision”活动,然后在“属性”面板中设置以下值。

    属性

    条件

    isProcessed

    FalseLabel

    已更新

    TrueLabel

    已处理

  17. 在工作流中,单击第二个“FlowDecision”活动,然后在“属性”面板中设置以下值。

    属性

    条件

    isShipped

    FalseLabel

    已更新

    TrueLabel

    已发售

    您已经定义了状态机工作流的主要结构。您现在将定义每个状态。

  18. 在工作流中,双击“等待订单”。注意,路径显示在选项卡下。您可以单击“订单服务”以返回到您会看到整个状态机工作流的页面。

  19. 将以下活动按显示顺序从工具箱拖动到工作流:

    类别 活动 注意

    基元

    Assign

    此分配活动将为订单获取一个订单 ID (GUID)。

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowSerivce

    SendNotification

    此自定义活动将电子邮件通知发送到客户端。

    基元

    Assign

    使用此分配活动,以便能够监控采购订单产品。

    基元

    Assign

    使用此分配活动,以便能够监控采购订单数量。

  20. 重新排列这些活动,使它看起来类似于如下所示:

    等待订单状态活动

  21. 单击工作流底部的“变量”打开“变量”面板。

  22. 在“变量”面板中,单击“创建变量”,然后创建以下变量:

    变量名 变量类型 作用域 注意

    product

    字符串

    等待订单

    AppFabric 可以跟踪工作流变量。创建此变量,以便能够跟踪产品名称。

    quantity

    Int32

    等待订单

    此变量用于进行跟踪。

  23. 在工作流中,单击“接收”活动,然后在“属性”面板中设置以下值。

    属性

    内容

    消息;消息数据:po;消息类型:Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderProcessingService.Purchaseorder

    DisplayName

    接收 PO

    OperationName

    SubmitPO

    SerivceContractName

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService

    CanCreateInstance

    (已选择)

  24. 在工作流中,单击第一个“分配”活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    分配 PO ID

    po.POID

    System.Guid.NewGuid().ToString()

  25. 在工作流中,单击“SendReplyToReceive”活动,然后在“属性”面板中设置以下值。

    属性

    内容

    消息;消息数据:po.POID;消息类型:字符串

    CorrelationInitializers

    CorrelationOrderWorkflow;Query correlation initialize; key1=sm:body()/xg0:string

    237be300-a94d-4b8e-a707-83f4d2041f6e

    备注

    您刚才的操作称为关联。此工作流具有以下七个接收活动:一个接收采购订单的活动、一个接收流程服务结果的活动、一个接收发售服务结果的活动、两个接收客户端更新的活动,还有两个接收客户端取消的活动。在收到采购订单之后,工作流服务会生成一个 GUID 号,作为订单编号。设想一下工作流服务同时收到多个采购订单的情况。工作流引擎会为每个采购订单请求创建一个工作流实例。工作流引擎需要将其他六个请求与工作流实例相匹配(或相关联)。为此,工作流引擎就要求每个关联都有一个唯一的标识符。在本示例中,唯一的标识符就是订单编号 (GUID)。在第一个 SendReplyToReceive 活动中,您将定义一个 CorrelationInitializer。您选择“查询相关初始值设定项”查询类型,该类型表示唯一的标识符在传递给接收活动的邮件中。您还将指定到该字段的 xPath。除了相关初始值设定项之外,您还必须在 CorrelateWith 字段中指定“相关句柄”。“相关句柄”是容纳相关数据的容器,因此可以在工作流中的任何位置检索相关数据。在后续接收中,您将指定相同的相关句柄。您将在 CorrelateOn 字段中指定如何从接收到的邮件中检索订单编号。

  26. 在工作流中,单击“SendNotification”活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "We have received your order.  Your order number is " + po.POID

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Received"

    To

    po.EmailAddress

  27. 在工作流中,单击第二个“分配”活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    指定产品名称

    To

    产品

    Value

    po.Description

  28. 在工作流中,单击第三个“分配”活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    指定数量

    To

    quantity

    Value

    po.Quantity

    您已经实现了等待订单状态。

  29. 在选项卡名称下,单击“订单服务”以显示“订单服务”流程图活动。

  30. 在工作流中,双击“订单已打开”序列活动以实现此状态。

  31. 将以下活动按显示顺序从工具箱拖动到工作流:

    类别 活动 注意

    基元

    分配

    控制流

    并行

  32. 将以下活动拖动到“并行”活动中:

    类别 活动 注意

    控制流

    序列

  33. 将以下活动拖动到“序列”活动中:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.Activities

    ProcessOrder

    运行时

    永久

    消息

    接收

    Microsoft.Samples.Dublin.Tutorials.OrderService.orderWorkflowService

    SendNotification

    基元

    分配

  34. 将以下活动拖动到 Parallel 活动中以及现有 Sequence 活动的右侧:

    类别 活动 注意

    消息

    ReceiveAndSendReply

    ReceiveAndSendReply 活动是包含 Sequence 活动(其中包括 Receive 活动和 SendReplyToReceive 活动)的复合活动。

  35. 将以下活动拖动到新添加的 Sequence 活动中并放在 Receive 和 SendReplyToReceive 这两个活动下:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.Activities

    CancelOrderProcess

    控制流

    If

  36. 将以下活动拖动到 If 活动的 Then 分支中:

    类别 活动 注意

    控制流

    Sequence

  37. 将以下活动拖动到新添加的 Sequence 活动中:

    类别 活动 注意

    基元

    分配

    基元

    分配

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SenNotification

  38. 将以下活动拖动到 If 活动的 Else 分支中:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SenNotification

  39. 将以下活动拖动到 Parallel 活动中,使之成为最右边的分支:

    类别 活动 注意

    消息

    ReceiveAndSendReply

  40. 将以下活动拖动到新添加的 Sequence 活动中并放在 Receive 和 SendReplyToReceive 这两个活动下:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.Activities

    CancelOrderProcess

    控制流

    If

  41. 将以下活动拖动到 If 活动的 Then 分支中:

    类别 活动 注意

    Control Flow

    Sequence

  42. 将以下活动拖动到新添加的 Sequence 活动中:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SenNotification

    运行时

    TerminateWorkflow

  43. 将以下活动拖动到 If 活动的 Else 分支中:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SenNotification

    在添加这些活动后,已打开订单的状态看起来类似于以下内容:

    已打开的订单状态

  44. 单击 Parallel 活动最左边分支上的 Sequence 活动,然后单击工作流底部的“变量”以打开“变量”面板。

  45. 在“变量”面板中,单击“创建变量”,然后创建以下变量:

    变量名 变量类型 作用域 注意

    cancelOrderProcessAcknowledgement

    字符串

    并行

    ProcessServiceResult

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.ProcessServiceResult

    序列

    processServiceAcknowledgement

    字符串

    序列

  46. 在工作流中,单击 Parallel 活动顶部的“Assign” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    初始化 isUpdated

    To

    isUpdated

    False

  47. 在工作流中,单击“Parallel” 活动,然后在“属性”面板中设置以下值。

    属性

    Completion

    isUpdated Or isProcessed

    DisplayName

    处理订单

  48. 在工作流中,单击 Parallel 活动最左边分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    处理

  49. 在工作流中,单击 Parallel 活动最左边分支上的“ProcessOrder” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    处理订单

    po

    po

    ProcessOrderResult

    processServiceAcknowledgement

  50. 在工作流中,单击 Parallel 活动最左边分支上的“Receive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:processServiceResult; Message type:Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.ProcessServiceResult

    DisplayName

    接收处理结果

    OperationName

    SubmitProcessResult

    ServiceContractName

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService

    CanCreateInstance

    (清除)

    CorrelationOn

    CorrelationWith:correlationOrderWorkflow; XPath Queries:key1=sm:body()/xg0:ProcessServiceResult/sg0:POID

    CorrelationWith

    correlationOrderWorkflow

  51. 在工作流中,单击 Parallel 活动最左边分支上的“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Order with order#" + po.POID + " has been processed, and is ready for shipping."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Processed"

    To

    po.EmailAddress
  52. 在工作流中,单击 Parallel 活动最左边分支上的“Assign” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    设置 isProcessed 标记

    To

    isProcessed

    True

    您已经配置完 Parallel 活动最左边的分支。

  53. 在工作流中,单击 Parallel 活动中间分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    等待更新

  54. 在工作流中,单击 Parallel 活动中间分支上的“Receive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:poUpdate; Message type:Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderProcessingService.PurchaseOrder

    DisplayName

    接收更新

    OperationName

    SubmitUpdate

    ServiceContractName

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService

    CanCreateInstance

    (清除)

    CorrelationOn

    CorrelationWith:correlationOrderWorkflow; XPath Queries:key1=sm:body()/xg0:PurchaseOrder/xg0:POID

    CorrelationWith

    correlationOrderWorkflow

  55. 在工作流中,单击 Parallel 活动中间分支上的“SendReplyToReceive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:“已收到 PO 更新”; 消息类型:字符串

    DisplayName

    确认接收更新

  56. 在工作流中,单击 Parallel 活动中间分支上的“CancelOrderProcess” 活动,然后在“属性”面板中设置以下值。

    属性

    CancelOrderProcessResult

    cancelOrderProcessAcknowledgement

    DisplayName

    取消订单处理

    orderID

    po.POID
  57. 在工作流中,单击 Parallel 活动中间分支上的“If” 活动,然后在“属性”面板中设置以下值。

    属性

    条件

    cancelOrderProcessAcknowledgement = "The order process has been terminated successfully."

    DisplayName

    检查取消状态

  58. 在工作流中,单击 If 活动 Then 分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    取消成功序列

  59. 在工作流中,单击 If 活动 Then 分支上的第一个“Assign” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    刷新 PO

    To

    po

    poUpdate
  60. 在工作流中,单击 If 活动 Then 分支上的第二个“Assign” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    设置 isUpdated 标记

    To

    isUpdated

    True
  61. 在工作流中,单击 If 活动 Then 分支上的“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Your order has updated upon your request. The order is under processing."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Updated (by customer)"

    To

    po.EmailAddress
  62. 在工作流中,单击 If 活动 Else 分支上的“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Your order update request cannot be processed. Please try again."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Update (by customer) Failed"

    To

    po.EmailAddress

    您已经配置完 Parallel 活动中间的分支。

  63. 在工作流中,单击 Parallel 活动最右边分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    等待取消

  64. 在工作流中,单击 Parallel 活动最右边分支上的“Receive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:poID; Message type:String

    DisplayName

    接收取消

    OperationName

    SubmitCancellation

    ServiceContractName

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService

    CanCreateInstance

    (清除)

    CorrelationOn

    CorrelationWith:correlationOrderWorkflow; XPath Queries:key1=sm:body()/xg0:string

    CorrelationWith

    correlationOrderWorkflow

  65. 在工作流中,单击 Parallel 活动最右边分支上的“SendReplyToReceive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:“PO cancellation received”; Message type:String

    DisplayName

    确认收到取消

  66. 在工作流中,单击 Parallel 活动最右边分支上的“CancelOrderProcess” 活动,然后在“属性”面板中设置以下值。

    属性

    CancelOrderProcessResult

    cancelOrderProcessAcknowledgement

    DisplayName

    取消订单处理

    orderID

    po.POID
  67. 在工作流中,单击 Parallel 活动最右边分支上的“If” 活动,然后在“属性”面板中设置以下值。

    属性

    条件

    cancelOrderProcessAcknowledgement = "The order process has been terminated successfully."

    DisplayName

    检查取消状态

  68. 在工作流中,单击 If 活动 Then 分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    取消成功序列

  69. 在工作流中,单击 If 活动 Then 分支上的“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Your order has been cancelled upon your request."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Cancelled (by customer)"

    To

    po.EmailAddress
  70. 在工作流中,单击 If 活动 Then 分支上的“TerminateWorkflow” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    终止工作流

    原因

    “Client cancellation”
  71. 在工作流中,单击 If 活动 Else 分支上的第二个“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Your order cancellation request cannot be processed. Please try again."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Cancellation(by customer) Failed"

    To

    po.EmailAddress

    您已经配置完 Parallel 活动最右边的分支。

  72. 在选项卡名称下,单击“订单服务”以显示“订单服务”流程图活动。

  73. 在工作流中,双击“订单已处理”序列活动以实现此状态。

  74. 将以下活动按显示顺序从工具箱拖动到工作流:

    类别 活动 注意

    基元

    分配

    控制流

    并行

  75. 将以下活动拖动到“并行”活动中:

    类别 活动 注意

    控制流

    序列

  76. 将以下活动拖动到“序列”活动中:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService.Activities

    ShipOrder

    运行时

    永久

    消息

    接收

    基元

    分配

  77. 将以下活动拖动到 Parallel 活动中以及现有 Sequence 活动的右侧:

    类别 活动 注意

    消息

    ReceiveAndSendReply

    ReceiveAndSendReply 活动是包含 Sequence 活动(其中包括 Receive 活动和 SendReplyToReceive 活动)的复合活动。

  78. 将以下活动拖动到新添加的 Sequence 活动中并放在 Receive 和 SendReplyToReceive 这两个活动下:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService.Activities

    CancelShipping

    控制流

    If

  79. 将以下活动拖动到 If 活动的 Then 分支中:

    类别 活动 注意

    控制流

    序列

  80. 将以下活动拖动到新添加的 Sequence 活动中:

    类别 活动 注意

    基元

    分配

    基元

    分配

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SendNotification

  81. 将以下活动拖动到 If 活动的 Else 分支中:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SendNotification

  82. 将以下活动拖动到 Parallel 活动中,使之成为最右边的分支:

    类别 活动 注意

    消息

    ReceiveAndSendReply

  83. 将以下活动拖动到新添加的 Sequence 活动中并放在 Receive 和 SendReplyToReceive 这两个活动下:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService.Activities

    CancelShipping

    控制流

    If

  84. 将以下活动拖动到 If 活动的 Then 分支中:

    类别 活动 注意

    控制流

    序列

  85. 将以下活动拖动到新添加的 Sequence 活动中:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SendNotification

    运行时

    TerminateWorkflow

  86. 将以下活动拖动到 If 活动的 Else 分支中:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SenNotification

    添加这些活动之后,“订单已处理”的状态看起来类似于以下内容:

    已处理的订单状态

  87. 单击 Parallel 活动最左边分支上的 Sequence 活动,然后单击工作流底部的“变量”以打开“变量”面板。

  88. 在“变量”面板中,单击“创建变量”,然后创建以下变量:

    变量名 变量类型 作用域 注意

    cancelShippingAcknowledgement

    字符串

    并行

    ShippingServiceResult

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.ShippingServiceResult

    序列

    shippingServiceAcknowledgement

    字符串

    序列

  89. 在工作流中,单击 Parallel 活动顶部的“Assign” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    初始化 isUpdated

    To

    isUpdated

    False

  90. 在工作流中,单击“Parallel” 活动,然后在“属性”面板中设置以下值。

    属性

    完成

    isUpdated Or isShipped

    DisplayName

    发售订单

  91. 在工作流中,单击 Parallel 活动最左边分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    发售

  92. 在工作流中,单击 Parallel 活动最左边分支上的“ShipOrder” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    发售订单

    poID

    po.POID

    ShipOrderResult

    shippingServiceAcknowledgement

  93. 在工作流中,单击 Parallel 活动最左边分支上的“Receive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:shippingServiceResult; Message type:Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.ShippingServiceResult

    DisplayName

    接收发售结果

    OperationName

    SubmitShippingResult

    ServiceContractName

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService

    CanCreateInstance

    (清除)

    CorrelationOn

    CorrelationWith:correlationOrderWorkflow; XPath Queries:key1=sm:body()/xg0:ShippingServiceResult/xg0:POID

    CorrelationWith

    correlationOrderWorkflow

  94. 在工作流中,单击 Parallel 活动最左边分支上的“Assign” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    设置 isShipped 标志

    To

    isShipped

    True

    您已经配置完 Parallel 活动最左边的分支。

  95. 在工作流中,单击 Parallel 活动中间分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    等待更新

  96. 在工作流中,单击 Parallel 活动中间分支上的“Receive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:poUpdate; Message type:Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderProcessingService.PurchaseOrder

    DisplayName

    接收更新

    OperationName

    SubmitUpdate

    ServiceContractName

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService

    CanCreateInstance

    (清除)

    CorrelationOn

    CorrelationWith:correlationOrderWorkflow; XPath Queries:key1=sm:body()/xg0:PurchaseOrder/xg0:POID

    CorrelationWith

    correlationOrderWorkflow

  97. 在工作流中,单击 Parallel 活动中间分支上的“SendReplyToReceive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:“PO update received”; Message type:String

    DisplayName

    确认接收更新

  98. 在工作流中,单击 Parallel 活动中间分支上的“CancelShipping” 活动,然后在“属性”面板中设置以下值。

    属性

    CancelShippingResult

    cancelShippingAcknowledgement

    DisplayName

    取消发售

    orderID

    poUpdate.POID
  99. 在工作流中,单击 Parallel 活动中间分支上的“If” 活动,然后在“属性”面板中设置以下值。

    属性

    条件

    cancelShippingAcknowledgement = "The shipping process has been terminated successfully."

    DisplayName

    检查取消状态

  100. 在工作流中,单击 If 活动 Then 分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    取消成功序列

  101. 在工作流中,单击 If 活动 Then 分支上的第一个“Assign” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    刷新 PO

    To

    po

    poUpdate
  102. 在工作流中,单击 If 活动 Then 分支上的第二个“Assign” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    设置 isUpdated 标记

    To

    isUpdated

    True
  103. 在工作流中,单击 If 活动 Then 分支上的“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Your order has updated upon your request. The order is under processing."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Updated (by customer)"

    To

    po.EmailAddress
  104. 在工作流中,单击 If 活动 Else 分支上的“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Your order update request cannot be processed. The order has been shipped."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Update (by customer) Failed"

    To

    po.EmailAddress

    您已经配置完 Parallel 活动中间的分支。

  105. 在工作流中,单击 Parallel 活动最右边分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    等待取消

  106. 在工作流中,单击 Parallel 活动最右边分支上的“Receive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:poID; Message type:String

    DisplayName

    接收取消

    OperationName

    SubmitCancellation

    ServiceContractName

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService

    CanCreateInstance

    (清除)

    CorrelationOn

    CorrelationWith:correlationOrderWorkflow; XPath Queries:key1=sm:body()/xg0:string

    CorrelationWith

    correlationOrderWorkflow

  107. 在工作流中,单击 Parallel 活动最右边分支上的“SendReplyToReceive” 活动,然后在“属性”面板中设置以下值。

    属性

    内容

    Message; Message data:“PO cancellation received”; Message type:String

    DisplayName

    确认收到取消

  108. 在工作流中,单击 Parallel 活动最右边分支上的“CancelShipping” 活动,然后在“属性”面板中设置以下值。

    属性

    CancelshippingResult

    cancelShippingAcknowledgement

    DisplayName

    取消发售

    poID

    po.POID
  109. 在工作流中,单击 Parallel 活动最右边分支上的“If” 活动,然后在“属性”面板中设置以下值。

    属性

    条件

    cancelShippingAcknowledgement = "The shipping process has been terminated successfully."

    DisplayName

    检查取消状态

  110. 在工作流中,单击 If 活动 Then 分支上的“Sequence” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    取消成功序列

  111. 在工作流中,单击 If 活动 Then 分支上的“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "We are sorry you chose to cancel your order. If there is anything we can do to help you with our order process or with our products or services please do not hesitate to contact us."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Cancelled (by cusotmer)"

    To

    po.EmailAddress
  112. 在工作流中,单击 If 活动 Then 分支上的“TerminateWorkflow” 活动,然后在“属性”面板中设置以下值。

    属性

    DisplayName

    终止工作流

    原因

    “Client cancellation”
  113. 在工作流中,单击 If 活动 Else 分支上的第二个“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Your order cancellation request cannot be processed. The order has been shipped.

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~Order Cancellation(by customer) Failed"

    To

    po.EmailAddress

    您已经配置完 Parallel 活动最右边的分支。

  114. 在选项卡名称下,单击“订单服务”以显示“订单服务”流程图活动。

  115. 在工作流中,双击“订单已完成”序列活动以实现此状态。

  116. 将以下活动按显示顺序从工具箱拖动到工作流:

    类别 活动 注意

    Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService

    SendNotification

  117. 在工作流中,单击 If 活动 Else 分支上的第二个“SendNotification” 活动,然后在“属性”面板中设置以下值。

    属性

    Body

    "Your order has been shipped.  Thank you for shopping at contoso.com."

    DisplayName

    发送客户端通知

    Subject

    "Contoso.com Order#" + po.POID + "~~ Order Shipped"

    To

    po.EmailAddress

    您已经完成工作流开发。

在此配置文件中,您将定义两个终结点和一个行为元素。

使用配置文件配置订单工作流服务的步骤

  1. 在“解决方案资源管理器”中,展开“OrderWorkflowService”,然后双击“Web.config”将其打开。

  2. <system.serviceModel> 标记中添加 <services> 标记。

        <services>
          <service name="Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderWorkflow">
            <endpoint address="" binding="basicHttpBinding" contract="Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService" />
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
          </service>
        </services>
    

编译订单工作流服务的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderWorkflowService” 项目,然后单击“重建”。确保此项目在“输出”窗口中成功编译。

完成订单处理服务

OrderProcessingService 和 OrderWorkflowService 相互引用。因此,您在完成 OrderWorkflowService 后必须完成 OrderProcessingService。

添加服务引用的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderProcessingService”,然后单击“添加服务引用”。

  2. 在“添加服务引用”中,单击“发现”。Visual Studio 应会发现这两个服务。

  3. 输入并选择以下值,然后单击“确定”以创建服务引用。

    属性

    服务

    OrderWorkflow.xamlx

    Namespace

    OrderWorkflowService

修改 OrderProcess.svc 的步骤

  1. 在“解决方案资源管理器”中,展开“OrderProcessingService”,然后双击“OrderProcessing.svc”将其打开。

  2. 取消对 SendProcessResult 函数中该节的注释,因此该函数看起来类似于如下所示:

    
    private void SendProcessResult(object state)
    {
        PurchaseOrder po = (PurchaseOrder)state;
    
        WorkItem workItem = new WorkItem(Thread.CurrentThread);
        WorkItemMap.Add(po.POID, workItem);
    
        //Simulating the order processing process
        Thread.Sleep(120000);
    
        //The following portion will be uncommented after referencing OrderWorkflowService
        OrderWorkflowService.ProcessServiceResult reply = new OrderWorkflowService.ProcessServiceResult();
        reply.POID = po.POID;
        reply.Message = "The order has been processed successfully.";
    
        lock (workItem.WorkItemLock)
        {
            using (OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient client = new OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient())
            {
                client.SubmitProcessResult(reply);
            }
    
            workItem.Complete();
            WorkItemMap.Remove(po.POID);
        }
    
    }
    

编译订单处理服务的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderProcessingService”项目,然后单击“重建”。确保此项目在“输出”窗口中成功编译。

完成发售服务

ShippingService 和 OrderWorkflowService 相互引用。因此,您在完成 OrderWorkflowService 后必须完成 ShipingService。

添加服务引用的步骤

  1. 在“解决方案资源管理器”中,右键单击“ShippingService”,然后单击“添加服务引用”。

  2. 在“添加服务引用”中,单击“发现”。Visual Studio 应会发现这两个服务。

  3. 输入并选择以下值,然后单击“确定”以创建服务引用。

    属性

    服务

    OrderWorkflow.xamlx

    Namespace

    OrderWorkflowService

修改 Shipping.svc 的步骤

  1. 在“解决方案资源管理器”中,展开“ShippingService”,然后双击“Shipping.svc”将其打开。

  2. 撤消对 SendShippingResult 函数中该节的注释,因此该函数看起来类似于如下所示:

    private void SendShippingResult(object state)
    {
        string poID = state.ToString();
    
        WorkItem workItem = new WorkItem(Thread.CurrentThread);
        WorkItemMap.Add(poID, workItem);
    
        //Simulating the order processing process
        Thread.Sleep(60000);
    
        //The following portion will be uncommented after referencing OrderWorkflowService
        OrderWorkflowService.ShippingServiceResult reply = new OrderWorkflowService.ShippingServiceResult();
        reply.POID = poID;
        reply.Message = "The order has been shipped.";
    
        lock (workItem.WorkItemLock)
        {
            using (OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient client = new OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient())
            {
                client.SubmitShippingResult(reply);
            }
    
            workItem.Complete();
            WorkItemMap.Remove(poID);
        }
    }
    

编译发售服务的步骤

  1. 在“解决方案资源管理器”中,右键单击“ShippingService”项目,然后单击“重建”。确保此项目在“输出”窗口中成功编译。

开发订单客户端应用程序

在此步骤中,您将开发 Windows 窗体客户端应用程序。

向解决方案中添加 Windows 窗体应用程序项目的步骤

  1. 在“解决方案资源管理器”中,右键单击“解决方案‘OrderService’”,指向“添加”,然后单击“新项目”。

  2. 在“添加新项目”中,选择或键入以下值,然后单击“确定”。

    属性

    项目类型

    Visual C#/Windows

    模板

    Windows 窗体应用程序

    名称

    OrderClient

    位置

    C:\DublinTutorial\OrderServiceSolution\OrderService

订单客户端是工作流服务的接口。您必须向工作流服务中添加服务引用。

添加服务引用的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderClient”,然后单击“添加服务引用”打开“添加服务引用”对话框。

  2. 在“添加服务引用”中,单击“发现”。

  3. 输入并选择以下值,然后单击“确定”以创建服务引用。

    属性

    服务

    OrderWorkflow.xamlx

    Namespace

    OrderWorkflowService

开发 Windows 窗体的步骤

  1. 在“解决方案资源管理器”中,展开“OrderClient”,然后双击“Form1.cs”将其打开。

  2. 右键单击“Form1.cs”,再单击“重命名”,然后键入“OrderForm.cs”。

  3. 出现提示时单击“是”。

  4. 从工具箱中添加四个“标记”控件、五个“TextBox”控件和三个“按钮”控件,并将这些控件对齐,使之看起来类似于以下屏幕截图:

    订单客户端表单

  5. 在工作流中,单击此窗体,然后在“属性”面板中设置以下值。

    属性

    Name

    OrderForm

    Text

    Contoso.com 订单窗体

  6. 在工作流中,单击“label1”,然后在“属性”面板中设置以下值。

    属性

    Name

    lblOrderNumber

    Text

    订单编号:

  7. 在工作流中,单击“label2”,然后在“属性”面板中设置以下值。

    属性

    Name

    lblEmail

    Text

    电子邮件:

  8. 在工作流中,单击“label3”,然后在“属性”面板中设置以下值。

    属性

    Name

    lblDescription

    Text

    描述:

  9. 在工作流中,单击“label4”,然后在“属性”面板中设置以下值。

    属性

    Name

    lblQuantity

    Text

    数量:

  10. 在工作流中,单击“textbox1”,然后在“属性”面板中设置以下值。

    属性

    Name

    txtOrderNumber

    已启用

    False

  11. 在工作流中,单击“textbox2”,然后在“属性”面板中设置以下值。

    属性

    Name

    txtEmail

    Text

    JohnDole@fabrikam.com

  12. 在工作流中,单击“textbox3”,然后在“属性”面板中设置以下值。

    属性

    Name

    txtDescription

    Text

    Windows 7

  13. 在工作流中,单击“textbox4”,然后在“属性”面板中设置以下值。

    属性

    Name

    txtQuantity

    Text

    10

  14. 在工作流中,单击“textbox5”,然后在“属性”面板中设置以下值。

    属性

    Name

    txtStatus

    Anchor

    底部,左侧,右侧

    已启用

    False

    Text

    “”

  15. 在工作流中,单击“button1”,然后在“属性”面板中设置以下值。

    属性

    Name

    btnSubmit

    Anchor

    底部,右侧

    Text

    Submit

    单击(在“事件”选项卡下)

    btnSubmit_Click

  16. 在工作流中,单击“button2”,然后在“属性”面板中设置以下值。

    属性

    Name

    btnUpdate

    Anchor

    底部,右侧

    Text

    更新

    单击(在“事件”选项卡下)

    btnUpdate_Click

  17. 在工作流中,单击“button3”,然后在“属性”面板中设置以下值。

    属性

    Name

    btnCancel

    Anchor

    底部,右侧

    Text

    取消

    单击(在“事件”选项卡下)

    btnCancel_Click

  18. 在“解决方案资源管理器”中,右键单击“OrderForm.cs”,然后单击“查看代码”。

  19. 右键单击“OrderClient”命名空间,单击“重构”,然后单击“重命名”以打开“重命名”对话框。

  20. 在“重命名”中,键入“Microsoft.Samples.Dublin.Tutorials.OrderService.OrderClient”,然后单击“确定”。

  21. 单击“应用”。

  22. 使用以下内容替换此代码:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace Microsoft.Samples.Dublin.Tutorials.OrderService.OrderClient
    {
        public partial class OrderForm : Form
        {
            //Delegates to make all service calls on a secondary thread
            private delegate void SubmitOrderDelegate();
            private delegate void CancelOrderDelegate();
            private delegate void UpdateOrderDelegate();
            private delegate void CallbackDelegate(string poID, string str);
    
            private SubmitOrderDelegate SubmitOrderHandler;
            private CancelOrderDelegate CancelOrderHandler;
            private UpdateOrderDelegate UpdateOrderHandler;
            private CallbackDelegate CallbackHandler;
    
            public OrderForm()
            {
                InitializeComponent();
            }
    
            private void OrderForm_Load(object sender, EventArgs e)
            {
                btnUpdate.Enabled = false;
                btnCancel.Enabled = false;
                btnSubmit.Focus();
            }
    
            #region Submit button
            private void btnSubmit_Click(object sender, EventArgs e)
            {
                btnSubmit.Enabled = false;
                btnCancel.Enabled = false;
                btnUpdate.Enabled = false;
                txtEmail.Enabled = false;
    
                txtStatus.Text = "Connecting to the Order service ...";
    
                //Executed on secondary thread so the UI thread is not blocked
                SubmitOrderHandler = new SubmitOrderDelegate(this.SubmitOrder);
                SubmitOrderHandler.BeginInvoke(null, null);
            }
    
            private void SubmitOrder()
            {
                string strMessage = "Your order has been received.";
                string strPOID = "";
    
                OrderWorkflowService.PurchaseOrder po = new OrderWorkflowService.PurchaseOrder();
                po.EmailAddress = txtEmail.Text;
                po.Description = txtDescription.Text;
                po.Quantity = System.Int32.Parse(txtQuantity.Text);
    
                //A Blocking service call executed on secondary thread
                try
                {
                    OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient client = new OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient();
                    strPOID = client.SubmitPO(po);
                    client.Close();
                }
                catch (Exception Ex)
                {
                    strMessage = "Error: " + Ex.Message;
                }
    
                //Make UI updates back on the primary thread
                CallbackHandler = new CallbackDelegate(this.SubmitOrderCallBack);
                this.BeginInvoke(CallbackHandler, strPOID, strMessage);
    
            }
    
            private void SubmitOrderCallBack(string strPOID, string strMessage)
            {
                //UI updates back on the primary thread
                btnUpdate.Enabled = true;
                btnCancel.Enabled = true;
    
                txtOrderNumber.Text = strPOID;
                txtStatus.Text = strMessage;
            }
            #endregion
    
            #region Update button
            private void btnUpdate_Click(object sender, EventArgs e)
            {
                btnUpdate.Enabled = false;
                btnCancel.Enabled = false;
    
                txtStatus.Text = "Connecting to the Order service ...";
    
                //Executed on secondary thread so the UI thread is not blocked
                UpdateOrderHandler = new UpdateOrderDelegate(this.UpdateOrder);
                UpdateOrderHandler.BeginInvoke(null, null);
    
            }
    
            private void UpdateOrder()
            {
                string strMessage = "Your order update request has been received.";
                string strPOID = "";
    
                OrderWorkflowService.PurchaseOrder po = new OrderWorkflowService.PurchaseOrder();
                po.POID = txtOrderNumber.Text;
                po.EmailAddress = txtEmail.Text;
                po.Description = txtDescription.Text;
                po.Quantity = System.Int32.Parse(txtQuantity.Text);
    
                try
                {
                    OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient client = new OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient();
                    strMessage = client.SubmitUpdate(po);
                    client.Close();
                }
                catch (Exception Ex)
                {
                    strMessage = "Error: " + Ex.Message;
                }
    
                //Make UI updates back on the primary thread
                CallbackHandler = new CallbackDelegate(this.UpdateOrderCallback);
                this.BeginInvoke(CallbackHandler, strPOID, strMessage);
    
            }
    
            private void UpdateOrderCallback(string strPOID, string strMessage)
            {
                //UI updates back on the primary thread
                btnUpdate.Enabled = true;
                btnCancel.Enabled = true;
    
                txtStatus.Text = strMessage;
            }
            #endregion
    
            #region Cancel button
            private void btnCancel_Click(object sender, EventArgs e)
            {
                btnUpdate.Enabled = false;
                btnCancel.Enabled = false;
    
                txtStatus.Text = "Connecting to the Order service ...";
    
                //Executed on secondary thread so the UI thread is not blocked
                CancelOrderHandler = new CancelOrderDelegate(this.CancelOrder);
                CancelOrderHandler.BeginInvoke(null, null);
            }
    
            private void CancelOrder()
            {
                string strInOut = txtOrderNumber.Text;
    
                try
                {
                    OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient client = new OrderWorkflowService.MicrosoftSamplesDublinTutorialsOrderServiceOrderWorkflowServiceIOrderWorkflowServiceClient();
                    client.SubmitCancellation(ref strInOut);
                    client.Close();
                }
                catch (Exception Ex)
                {
                    strInOut = "Error: " + Ex.Message;
                }
    
                //Make UI updates back on the primary thread
                CallbackHandler = new CallbackDelegate(this.CancelOrderCallback);
                this.BeginInvoke(CallbackHandler, txtOrderNumber.Text, strInOut);
            }
    
            private void CancelOrderCallback(string strPOID, string strMessage)
            {
                //UI updates back on the primary thread
                //btnUpdate.Enabled = true;
                //btnCancel.Enabled = true;
    
                txtStatus.Text = strMessage;
            }
            #endregion
        }
    }
    

编译订单客户端的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderClient” 项目,然后单击“重建”。确保此项目在“输出”窗口中成功编译。

测试订单服务

测试订单服务的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderClient”,然后单击“设置为启动项目”。

  2. 在 Visual Studio 中,单击“调试”菜单,然后单击“开始调试”。您应会看到打开一个 Windows 窗体。

  3. 在该窗体中,单击“提交”。

  4. 打开 Windows 资源管理器并浏览到 C:\DublinTutorial\Inbox 文件夹。

  5. 请稍等,直到您看到全部三个电子邮件通知。看到全部这三个文件大概要用三到四分钟的时间。

将订单服务打包

将 OrderProcessingService WCF 服务打包的步骤

  1. 在“解决方案资源管理器”中,右键单击“OrderProcessingService” 项目,然后单击“打包/发布设置”。

  2. 输入以下值:

    属性

    以 ZIP 文件的形式创建一个 Web 包

    (已选择)

    创建该数据包的位置

    C:\DublinTutorial\DeploymentPackages\OrderProcessingService.zip

    要在目标服务器上使用的 IIS 网站/应用程序名称

    OrderService/OrderProcessingService

  3. 在“解决方案资源管理器”中,右键单击“OrderProcessingService”项目,然后单击“创建包”。

  4. 使用以下设置重复上述过程,为其他三个项目创建程序包:

    项目名称 创建该数据包的位置 要在目标服务器上使用的 IIS 网站/应用程序名称

    OrderWorkflowService

    C:\DublinTutorial\DeploymentPackages\OrderWorkflowService.zip

    OrderService/OrderWorkflowService

    ShippingService

    C:\DublinTutorial\DeploymentPackages\ShippingService.zip

    OrderService/ShippingService

    备注

    您可以考虑更新 Web.config 文件中相关服务的终结点地址,以反映打包服务之前部署程序包的服务器。

另请参阅

概念

Windows Server AppFabric 界面的使用教程
使用 Windows PowerShell 的教程

  2011-12-05