Appendix A: Developing the Order Application
This appendix provides the detailed steps for developers to build the Contoso Order service. The purpose of this appendix is to familiarize developers with developing applications that include Windows Communication Foundation (WCF) and/or Windows Workflow Foundation (WF) services using by Visual Studio 1010 and the .NET Framework 4. The main portion of the tutorial uses this application to show system administrators or application owners how to use Windows Server AppFabric to deploy, monitor, and troubleshoot applications that include WCF and/or WF services.
This Contoso Order service consists of the following four applications:
Order Process service: A WCF service that simulates calling an existing order processing application through the Web service interfaces.
Shipping service: A WCF service that simulates calling an existing shipping application through APIs.
Order Workflow service: A WF workflow service that manages the order process, including receiving the order, processing the order, and shipping the order.
Order client: A Windows Form application that serves as the front end to the Order service.
Note
The Appendix does not require you to install Windows Server AppFabric. But you must first install the Tutorial Using the Windows Server AppFabric Interface files to create the proper file structure for the Appendix to build correctly. Note that you are not required to go through and complete the Tutorial Using the Windows Server AppFabric Interface tutorial first for the Appendix - just install the files. For the installation procedure of the Tutorial Using the Windows Server AppFabric Interface tutorial, see Lesson 1: Getting Started. The C:\DublinTutorial\OrderServiceSolution\Completed folder contains a copy of the completed solution.
Procedure
You will go through the following steps to create the application:
Developing the Order Processing WCF service
Developing the Shipping WCF service
Developing the Order Workflow WF service
Finishing the Order Processing WCF service
Finishing the Shipping WCF service
Developing the Order client application
Packaging the Order service
Developing the Order Processing WCF Service
The Order Processing application is an application that Contoso purchased. It comes with Web services that other applications can communicate with. As a Contoso developer, you need to develop a WCF service called OrderProcessingService to interact with the Order Processing application.
To create a Visual Studio solution and a WCF Service Application for OrderProcessingService
Click Start, point to All Programs, point to Microsoft Visual Studio 2010, and then click Microsoft Visual Studio 2010.
From the File menu, click New, and then click New Project.
From New Project, choose or type the following values, and then click OK.
Property Value Project types
Visual C#/Web
Templates
WCF Service Application
Name
OrderProcessingService
Location
C:\DublinTutorial\OrderServiceSolution
Solution Name
OrderService
Create directory for solution
(selected)
From Solution Explorer, expand OrderProcessingService, right-click IService1.cs, and then click Delete.
Click OK to confirm deleting the file permanently.
From Solution Explorer, expand OrderProcessingService, right-click Service1.svc, and then click Delete.
Click OK to confirm deleting the file permanently.
The first task for creating a WCF service is to define a contract. The contract specifies what operations the service supports. An operation can be thought of as a Web service method. Each method in the interface corresponds to a specific service operation. In OrderProcessingService, you define two methods, ProcessOrder and CancelOrderProcess.
To define the service contract and data contract for the Order Processing service
From Solution Explorer, right-click OrderProcessService, point to Add, and then click New Item.
From Add New Item - OrderProcessService, select or type the following values, and then click Add.
Property Value Categories
Visual C#/Web
Template
WCF Service
Name
OrderProcessing.svc
Two files are added to the solution: IOrderProcessing.cs and OrderProcessing.svc.
From Solution Explorer, double-click IOrderProcessing.cs to open it.
Right-click the OrderProcessingService namespace, click Refactor, and then click Rename to open the Rename dialog box.
In New name, type Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService, and then click OK.
Click Apply, and then click Yes.
Modify the OrderProcessing.svc source code, so it looks like:
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; } }
In this file, you define the data contracts and the service contracts. Clients can call the service to process an order and to cancel the order processing.
After you create the contracts, which are defined by using an interface, the next step is to implement the interface. This involves creating a class called OrderProcessService that implements the user-defined IOrderProcessing interface.
To implement the Order Process service contract
From Solution Explorer, double-click IOrderProcessing.cs to open it.
Modify the source code, so it looks like:
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; } } }
Using a configuration file gives you the flexibility of providing endpoint and service behavior data at the point of deployment instead of at design time. In this configuration file, you define two endpoints.
To configure the Order Process service by using a configuration file
From Solution Explorer, expand OrderProcessingService, and then double-click Web.config to open it. Add a <services> tag inside the <system.serviceModel> tag:
<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>
To compile the Order Process WCF service
- From Solution Explorer, right-click the OrderProcessingService project, and then click Rebuild. Make sure the project is compiled successfully in the Output window.
To test the Order Process WCF service
From Solution Explorer, right-click the OrderProcessingService project, and then click View in Browser. An Internet Explorer window opens listing the directory files.
From the Internet Explorer window, click OrderProcessing.svc. Make sure you don’t get any errors.
Developing the Shipping WCF Service
The Shipping service is a WCF service calling a SQL Server store. In the simulation, you don’t really make a database call.
To add a new WCF Service Application project to the solution
From Solution Explorer, right-click Solution ‘OrderService’, point to Add, and then click New Project.
From Add New Project, choose or type the following values, and then click OK.
Property Value Project types
Visual C#/Web
Templates
WCF Service Application
Name
ShippingService
Location
C:\DublinTutorial\OrderServiceSolution\OrderService
From Solution Explorer, expand ShippingService, right-click IService1.cs, and then click Delete.
Click OK to confirm deleting the file permanently.
From Solution Explorer, expand ShippingService, right-click Service1.svc, and then click Delete.
Click OK to confirm deleting the file permanently.
You define one service contract called IShipping, which contains two operation contracts, ShipOrder and CancelShipping.
To define the Shipping WCF service contract
From Solution Explorer, right-click ShippingService, point to Add, and then click New Item.
From Add New Item - ShippingService, select or type the following values, and then click Add.
Property Value Categories
Visual C#/Web
Template
WCF Service
Name
Shipping.svc
Two files are added to the project, IShipping.cs and Shipping.svc.
From Solution Explorer, double-click IShipping.cs to open it.
Right-click the ShippingService namespace, click Refactor, and then click Rename to open the Rename dialog box.
In New name, type Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService, and then click OK.
Click Apply, and then click Yes.
Modify the source code, so it looks like:
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); } }
To implement the Shipping WCF service contract
From Solution Explorer, double-click Shipping.svc to open it.
Modify the source code, so it looks like:
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; } } }
In this configuration file, you define two endpoints.
To configure the Shipping WCF service by using a configuration file
From Solution Explorer, expand ShippingService, and then double-click Web.config to open it. Add a <services> tag inside the <system.serviceModel> tag:
<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>
To compile the Shipping WCF service
- From Solution Explorer, right-click the ShippingService project, and then click Rebuild. Make sure the project is compiled successfully in the Output window.
Developing the Order Workflow WF Service
The Order workflow service application is the major portion of the whole service. It orchestrates the whole business process. It receives a purchase order, then it calls OrderProcessingService and ShippingService, and finally it sends an e-mail message to the customer about the purchase order status.
To add a new WCF Workflow Service Application project to the solution
From Solution Explorer, right-click Solution ‘OrderService’, point to Add, and then click New Project.
From Add New Project, choose or type the following values, and then click OK.
Property Value Project types
Visual C#/Workflow
Templates
WCF Workflow Service Application
Name
OrderWorkflowService
Location
C:\DublinTutorial\OrderServiceSolution\OrderService
OrderWorkflowService consumes OrderProcessingService and ShippingService. You must reference the two services.
To add the service references
From Solution Explorer, right-click OrderWorkflowService, and then click Add Service Reference.
From Add Service Reference, click Discover. Visual Studio should discover both of the services.
Enter and select the following values, and then click OK to create the service reference.
Property Value Services
OrderProcessing.svc
Namespace
OrderProcessService
Repeat the steps to add another service reference with the following values:
Property Value Services
Shipping
Namespace
ShippingService
You need to define a custom workflow activity to use to simulate sending e-mail notifications.
To create a workflow code activity
From Solution Explorer, right-click OrderWorkflowService, point to Add, and then click New Item.
From Add New Item - OrderWorkflowService, select or type the following values, and then click Add.
Property Value Categories
Visual C#/Workflow
Template
Code Activity
Name
SendNotification.cs
From Solution Explorer, double-click SendNotification.cs to open it.
Right-click the OrderWorkflowService namespace, click Refactor, and then click Rename to open the Rename dialog box.
In New name, type Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService, and then click OK.
Click Apply, and then click Yes.
Modify the source code, so it looks like:
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); } } } }
Notice that the path is hardcoded to “C:\DublinTutorial\Inbox\”.
In the following procedure, you define the data types. These data types are used for OrderProcessingService and ShippingService to send results back to OrderWorkflowService.
To define data types
From Solution Explorer, right-click OrderWorkflowService, point to Add, and then click New Item.
From Add New Item - OrderWorkflowService, select or type the following values, and then click Add.
Property Value Categories
Visual C#/Code
Template
Code File
Name
DataTypes.cs
From Solution Explorer, double-click DataTypes.cs to open it.
Modify the source code, so it looks like:
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; } }
From Solution Explorer, right-click OrderWorkflowService, and then click Rebuild. You must build the project so the code activity is accessible from the workflow that you will develop in the following step. Compilation can also expose the referenced WCF service endpoints to the workflow.
The next step is to define the workflow. The workflow contains several states. You start with defining the states, and then define the details of the states. Each part of the development can contain the following steps:
Compose the workflow by using activities.
Define variables.
Configure the activities.
To define the workflow
From Solution Explorer, expand OrderWorkflowService, right-click Service1.xamlx, and then click Rename. Rename the file to OrderWorkflow.xamlx.
From Solution Explorer, double-click OrderWorkflow.xamlx to open it. There are two activities listed in the Sequential Service: one is called ReceiveRequest, and the other is SendResponse.
Right-click the Sequential Service activity, and then click Delete.
From the workflow, click Drop activity here, and set the following values in the Properties panel.
Property Value configurationName
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderWorkflow
Name
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderWorkflow
Click Toolbox on the left of the window to open the Toolbox panel, and then pin it to the left side of the window.
Drag the following activities from Toolbox to the workflow where it says “Drop activity here”.
Category Activity Note Flowchart
Flowchart
The Flowchart activity is a composite activity containing a Start by default. You will add more activities to it in the following steps.
Drag the following activities from Toolbox to the workflow in the order shown:
Category Activity Note Messaging
ReceiveAndSendReply
This is “Wait for order” state.
Control Flow
Sequence
This will contain the second state “Order opened”.
Flowchart
FlowDecision
The FlowDecision activity helps transitioning between states.
Control Flow
Sequence
This will contain the third state “Order processed”.
Flowchart
FlowDecision
The FlowDecision activity helps transitioning between states.
Control Flow
Sequence
This will contain the last state “Order completed”.
Use the mouse pointer to connect the activities so they look like:
Click Variables on the bottom of the workflow to open the Variables panel.
In the Variables panel, click Create Variable, and then create the following variables:
Variable name Variable type Scope Note poID
String
Flowchart
poID is a GUID number. poID is also used for correlation.
isProcessed
Boolean
Flowchart
It is a flag indicating whether an order has been processed successfully.
isShipped
Boolean
Flowchart
It is a flag indicating whether an order has been shipped.
isUpdated
Boolean
Flowchart
It is a flag indicating whether an order has been updated by customer.
po
PurchaseOrder
Flowchart
This is a custom data type defined in OrderProcessingService. It contains PO information.
poUpdate
PurchaseOrder
Flowchart
This contains the updated PO information when customer wants to update the PO.
correlationorderWorkflow
CorrelationHandle
Flowchart
This is the correlation handle that is used for both client connecting to the service, and the service connecting to OrderProcessService and ShippingService.
Here is a screenshot of the variables created:
From the workflow, click the Flowchart activity, and set the following values in the Properties panel.
Property Value DisplayName
Order service
From the workflow, click the first Sequence activity, and set the following values in the Properties panel.
Property Value DisplayName
Wait for order
From the workflow, click the second Sequence activity, and set the following values in the Properties panel.
Property Value DisplayName
Order opened
From the workflow, click the third Sequence activity, and set the following values in the Properties panel.
Property Value DisplayName
Order processed
From the workflow, click the fourth Sequence activity, and set the following values in the Properties panel.
Property Value DisplayName
Order completed
From the workflow, click the first FlowDecision activity, and set the following values in the Properties panel.
Property Value Condition
isProcessed
FalseLabel
Updated
TrueLabel
Processed
From the workflow, click the second FlowDecision activity, and set the following values in the Properties panel.
Property Value Condition
isShipped
FalseLabel
Updated
TrueLabel
Shipped
You have defined the main structure of the state machine workflow. You will now define each of the states.
From the workflow, double-click Wait for order. Notice the path is shown under the tab. You can click Order service to go back to the page where you see the whole state machine workflow.
Drag the following activities from Toolbox to the workflow in the order shown:
Category Activity Note Primitives
Assign
This Assign activity gets the order an order ID (GUID).
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowSerivce
SendNotification
This custom activity sends e-mail notification to client.
Primitives
Assign
This Assign activity is used so that purchase order product can be monitored.
Primitives
Assign
This Assign activity is used so that purchase order quantity can be monitored.
Rearrange the activities so they look like:
Click Variables on the bottom of the workflow to open the Variables panel.
In the Variables panel, click Create Variable, and then create the following variables:
Variable name Variable type Scope Note product
String
Wait for order
AppFabric is capable of tracking workflow variables. This variable is created so the product name can be tracked.
quantity
Int32
Wait for order
This variable is for tracking purpose.
From the workflow, click the Receive activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data: po; Message type: Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderProcessingService.Purchaseorder
DisplayName
Receive PO
OperationName
SubmitPO
SerivceContractName
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService
CanCreateInstance
(selected)
From the workflow, click the first Assign activity, and set the following values in the Properties panel.
Property Value DisplayName
Assign PO ID
To
po.POID
Value
System.Guid.NewGuid().ToString()
From the workflow, click the SendReplyToReceive activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data: po.POID; Message type: string
CorrelationInitializers
CorrelationOrderWorkflow;Query correlation initialize; key1=sm:body()/xg0:string
Note
What you just did is called correlation. This workflow has seven receive activities: one receiving the purchase order, one receiving the process service result, one receiving the shipping service result, two receiving client updates, and two receiving client cancellations. After receiving the purchase order, the workflow service generates a GUID number as the order number. Imagine that the workflow service receives many purchase orders at the same time. The workflow engine creates a workflow instance for each of the purchase order requests. The workflow engine needs to match (or correlate) the other six requests to the workflow instances. To accomplish this, the workflow engine needs a unique identifier for each correlation. In this sample, the unique identifier is the order number (GUID). In the first SendReplyToReceive activity, you define a CorrelationInitializer. You choose the Query Correlation Initializer query type, which means that the unique identifier is in the message that is passed to the receive activity. You also specify the xPath to the field. In addition to the Correlation Initializer, you must also specify a Correlation Handle in the CorrelateWith field. A Correlation Handle is a container for the correlation data, so the correlation data can be retrieved anywhere within the workflow. In the subsequent receives, you specify the same Correlation Handle. And in the CorrelateOn fields, you specify how to retrieve the order number from the message received.
From the workflow, click the SendNotification activity, and set the following values in the Properties panel.
Property Value Body
"We have received your order. Your order number is " + po.POID
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Received"
To
po.EmailAddress
From the workflow, click the second Assign activity, and set the following values in the Properties panel.
Property Value DisplayName
Assign product name
To
proudct
Value
po.Description
From the workflow, click the third Assign activity, and set the following values in the Properties panel.
Property Value DisplayName
Assign quantity
To
quantity
Value
po.Quantity
You have finished implementing the Wait for order state.
Under the tab name, click Order service to display the “Order service” Flowchart activity.
From the workflow, double-click the Order opened Sequence activity to implement the state.
Drag the following activities from Toolbox to the workflow in the order shown:
Category Activity Note Primitives
Assign
Control Flow
Parallel
Drag the following activity inside the Parallel activity:
Category Activity Note Control Flow
Sequence
Drag the following activity inside the Sequence activity:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.Activities
ProcessOrder
Runtime
Persist
Messaging
Receive
Microsoft.Samples.Dublin.Tutorials.OrderService.orderWorkflowService
SendNotification
Primitives
Assign
Drag the following activity inside the Parallel activity and on the right of the existing Sequence activity:
Category Activity Note Messaging
ReceiveAndSendReply
The ReceiveAndSendReply activity is a composite activity with one Sequence activity wrapping a Receive activity and a SendReplyToReceive activity.
Drag the following activities inside the newly added Sequence activity and beneath the two Receive and SendReplyToReceive activities:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.Activities
CancelOrderProcess
Control Flow
If
Drag the following activity inside the Then branch of the If activity:
Category Activity Note Control Flow
Sequence
Drag the following activities inside the newly added Sequence activity:
Category Activity Note Primitives
Assign
Primitives
Assign
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SenNotification
Drag the following activity inside the Else branch of the If activity:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SenNotification
Drag the following activity inside the Parallel activity to make it the rightmost branch:
Category Activity Note Messaging
ReceiveAndSendReply
Drag the following activities inside the newly added Sequence activity and beneath the two Receive and SendReplyToReceive activities:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderProcessingService.Activities
CancelOrderProcess
Control Flow
If
Drag the following activity inside the Then branch of the If activity:
Category Activity Note Control Flow
Sequence
Drag the following activities inside the newly added Sequence activity:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SenNotification
Runtime
TerminateWorkflow
Drag the following activity inside the Else branch of the If activity:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SenNotification
The Order Opened state looks like the following after adding the activities:
Click the Sequence activity on the leftmost branch of the Parallel activity, and then click Variables on the bottom of the workflow to open the Variables panel.
In the Variables panel, click Create Variable, and then create the following variables:
Variable name Variable type Scope Note cancelOrderProcessAcknowledgement
String
Parallel
ProcessServiceResult
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.ProcessServiceResult
Sequence
processServiceAcknowledgement
String
Sequence
From the workflow, click the Assign activity on the top of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Initialize isUpdated
To
isUpdated
Value
False
From the workflow, click the Parallel activity, and set the following values in the Properties panel.
Property Value Completion
isUpdated Or isProcessed
DisplayName
Process order
From the workflow, click the Sequence activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Processing
From the workflow, click the ProcessOrder activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Process order
po
po
ProcessOrderResult
processServiceAcknowledgement
From the workflow, click the Receive activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data:processServiceResult; Message type: Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.ProcessServiceResult
DisplayName
Receive process result
OperationName
SubmitProcessResult
ServiceContractName
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService
CanCreateInstance
(Clear)
CorrelationOn
CorrelationWith: correlationOrderWorkflow; XPath Queries: key1=sm:body()/xg0:ProcessServiceResult/sg0:POID
CorrelationWith
correlationOrderWorkflow
From the workflow, click the SendNotification activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Body
"Order with order#" + po.POID + " has been processed, and is ready for shipping."
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Processed"
To
po.EmailAddress
From the workflow, click the Assign activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Set isProcessed flag
To
isProcessed
Value
True
You have finished configuring the leftmost branch of the Parallel activity.
From the workflow, click the Sequence activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Waiting for update
From the workflow, click the Receive activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data:poUpdate; Message type: Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderProcessingService.PurchaseOrder
DisplayName
Receive update
OperationName
SubmitUpdate
ServiceContractName
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService
CanCreateInstance
(Clear)
CorrelationOn
CorrelationWith: correlationOrderWorkflow; XPath Queries: key1=sm:body()/xg0:PurchaseOrder/xg0:POID
CorrelationWith
correlationOrderWorkflow
From the workflow, click the SendReplyToReceive activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data: “PO update received”; Message type: String
DisplayName
Acknowledge receiving update
From the workflow, click the CancelOrderProcess activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value CancelOrderProcessResult
cancelOrderProcessAcknowledgement
DisplayName
Cancel order processing
orderID
po.POID
From the workflow, click the If activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Condition
cancelOrderProcessAcknowledgement = "The order process has been terminated successfully."
DisplayName
Check cancellation status
From the workflow, click the Sequence activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Cancellation succeed sequence
From the workflow, click the first Assign activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Refresh PO
To
po
Value
poUpdate
From the workflow, click the second Assign activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Set isUpdated flag
To
isUpdated
Value
True
From the workflow, click the SendNotification activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value Body
"Your order has updated upon your request. The order is under processing."
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Updated (by customer)"
To
po.EmailAddress
From the workflow, click the SendNotification activity on the Else branch of the If activity, and set the following values in the Properties panel.
Property Value Body
"Your order update request cannot be processed. Please try again."
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Update (by customer) Failed"
To
po.EmailAddress
You have finished configuring the middle branch of the Parallel activity.
From the workflow, click the Sequence activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Waiting for cancellation
From the workflow, click the Receive activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data:poID; Message type:String
DisplayName
Receive cancellation
OperationName
SubmitCancellation
ServiceContractName
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService
CanCreateInstance
(Clear)
CorrelationOn
CorrelationWith: correlationOrderWorkflow; XPath Queries: key1=sm:body()/xg0:string
CorrelationWith
correlationOrderWorkflow
From the workflow, click the SendReplyToReceive activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data: “PO cancellation received”; Message type: String
DisplayName
Acknowledge receiving cancellation
From the workflow, click the CancelOrderProcess activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value CancelOrderProcessResult
cancelOrderProcessAcknowledgement
DisplayName
Cancel order processing
orderID
po.POID
From the workflow, click the If activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Condition
cancelOrderProcessAcknowledgement = "The order process has been terminated successfully."
DisplayName
Check cancellation status
From the workflow, click the Sequence activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Cancellation succeed sequence
From the workflow, click the SendNotification activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value Body
"Your order has been cancelled upon your request."
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Cancelled (by customer)"
To
po.EmailAddress
From the workflow, click the TerminateWorkflow activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Terminate workflow
Reason
“Client cancellation”
From the workflow, click the second SendNotification activity on the Else branch of the If activity, and set the following values in the Properties panel.
Property Value Body
"Your order cancellation request cannot be processed. Please try again."
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Cancellation(by customer) Failed"
To
po.EmailAddress
You have finished configuring the rightmost branch of the Parallel activity.
Under the tab name, click Order service to display the “Order service” Flowchart activity.
From the workflow, double-click the Order processed Sequence activity to implement the state.
Drag the following activities from Toolbox to the workflow in the order shown:
Category Activity Note Primitives
Assign
Control Flow
Parallel
Drag the following activity inside the Parallel activity:
Category Activity Note Control Flow
Sequence
Drag the following activity inside the Sequence activity:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService.Activities
ShipOrder
Runtime
Persist
Messaging
Receive
Primitives
Assign
Drag the following activity inside the Parallel activity and on the right of the existing Sequence activity:
Category Activity Note Messaging
ReceiveAndSendReply
The ReceiveAndSendReply activity is a composite activity with one Sequence activity wrapping a Receive activity and a SendReplyToReceive activity.
Drag the following activities inside the newly added Sequence activity and beneath the two Receive and SendReplyToReceive activities:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService.Activities
CancelShipping
Control Flow
If
Drag the following activity inside the Then branch of the If activity:
Category Activity Note Control Flow
Sequence
Drag the following activities inside the newly added Sequence activity:
Category Activity Note Primitives
Assign
Primitives
Assign
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SendNotification
Drag the following activity inside the Else branch of the If activity:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SendNotification
Drag the following activity inside the Parallel activity to make it the rightmost branch:
Category Activity Note Messaging
ReceiveAndSendReply
Drag the following activities inside the newly added Sequence activity and beneath the two Receive and SendReplyToReceive activities:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.ShippingService.Activities
CancelShipping
Control Flow
If
Drag the following activity inside the Then branch of the If activity:
Category Activity Note Control Flow
Sequence
Drag the following activities inside the newly added Sequence activity:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SendNotification
Runtime
TerminateWorkflow
Drag the following activity inside the Else branch of the If activity:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SenNotification
The Order Processed state looks like the following after adding the activities:
Click the Sequence activity on the leftmost branch of the Parallel activity, and then click Variables on the bottom of the workflow to open the Variables panel.
In the Variables panel, click Create Variable, and then create the following variables:
Variable name Variable type Scope Note cancelShippingAcknowledgement
String
Parallel
ShippingServiceResult
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.ShippingServiceResult
Sequence
shippingServiceAcknowledgement
String
Sequence
From the workflow, click the Assign activity on the top of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Initialize isUpdated
To
isUpdated
Value
False
From the workflow, click the Parallel activity, and set the following values in the Properties panel.
Property Value Completion
isUpdated Or isShipped
DisplayName
Ship order
From the workflow, click the Sequence activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Shipping
From the workflow, click the ShipOrder activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Ship order
poID
po.POID
ShipOrderResult
shippingServiceAcknowledgement
From the workflow, click the Receive activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data:shippingServiceResult; Message type: Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.ShippingServiceResult
DisplayName
Receive shipping result
OperationName
SubmitShippingResult
ServiceContractName
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService
CanCreateInstance
(Clear)
CorrelationOn
CorrelationWith: correlationOrderWorkflow; XPath Queries: key1=sm:body()/xg0:ShippingServiceResult/xg0:POID
CorrelationWith
correlationOrderWorkflow
From the workflow, click the Assign activity on the leftmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Set isShipped flag
To
isShipped
Value
True
You have finished configuring the leftmost branch of the Parallel activity.
From the workflow, click the Sequence activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Waiting for update
From the workflow, click the Receive activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data:poUpdate; Message type: Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.OrderProcessingService.PurchaseOrder
DisplayName
Receive update
OperationName
SubmitUpdate
ServiceContractName
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService
CanCreateInstance
(Clear)
CorrelationOn
CorrelationWith: correlationOrderWorkflow; XPath Queries: key1=sm:body()/xg0:PurchaseOrder/xg0:POID
CorrelationWith
correlationOrderWorkflow
From the workflow, click the SendReplyToReceive activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data: “PO update received”; Message type: String
DisplayName
Acknowledge receiving update
From the workflow, click the CancelShipping activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value CancelShippingResult
cancelShippingAcknowledgement
DisplayName
Cancel shipping
orderID
poUpdate.POID
From the workflow, click the If activity on the middle branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Condition
cancelShippingAcknowledgement = "The shipping process has been terminated successfully."
DisplayName
Check cancellation status
From the workflow, click the Sequence activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Cancellation succeed sequence
From the workflow, click the first Assign activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Refresh PO
To
po
Value
poUpdate
From the workflow, click the second Assign activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Set isUpdated flag
To
isUpdated
Value
True
From the workflow, click the SendNotification activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value Body
"Your order has updated upon your request. The order is under processing."
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Updated (by customer)"
To
po.EmailAddress
From the workflow, click the SendNotification activity on the Else branch of the If activity, and set the following values in the Properties panel.
Property Value Body
"Your order update request cannot be processed. The order has been shipped."
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Update (by customer) Failed"
To
po.EmailAddress
You have finished configuring the middle branch of the Parallel activity.
From the workflow, click the Sequence activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value DisplayName
Waiting for cancellation
From the workflow, click the Receive activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data:poID; Message type:String
DisplayName
Receive cancellation
OperationName
SubmitCancellation
ServiceContractName
Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService.IOrderWorkflowService
CanCreateInstance
(Clear)
CorrelationOn
CorrelationWith: correlationOrderWorkflow; XPath Queries: key1=sm:body()/xg0:string
CorrelationWith
correlationOrderWorkflow
From the workflow, click the SendReplyToReceive activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Content
Message; Message data: “PO cancellation received”; Message type: String
DisplayName
Acknowledge receiving cancellation
From the workflow, click the CancelShipping activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value CancelshippingResult
cancelShippingAcknowledgement
DisplayName
Cancel shipping
poID
po.POID
From the workflow, click the If activity on the rightmost branch of the Parallel activity, and set the following values in the Properties panel.
Property Value Condition
cancelShippingAcknowledgement = "The shipping process has been terminated successfully."
DisplayName
Check cancellation status
From the workflow, click the Sequence activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Cancellation succeed sequence
From the workflow, click the SendNotification activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value 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
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Cancelled (by cusotmer)"
To
po.EmailAddress
From the workflow, click the TerminateWorkflow activity on the Then branch of the If activity, and set the following values in the Properties panel.
Property Value DisplayName
Terminate workflow
Reason
“Client cancellation”
From the workflow, click the second SendNotification activity on the Else branch of the If activity, and set the following values in the Properties panel.
Property Value Body
"Your order cancellation request cannot be processed. The order has been shipped.
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~Order Cancellation(by customer) Failed"
To
po.EmailAddress
You have finished configuring the rightmost branch of the Parallel activity.
Under the tab name, click Order service to display the “Order service” Flowchart activity.
From the workflow, double-click the Order completed Sequence activity to implement the state.
Drag the following activities from Toolbox to the workflow in the order as shown:
Category Activity Note Microsoft.Samples.Dublin.Tutorials.OrderService.OrderWorkflowService
SendNotification
From the workflow, click the second SendNotification activity on the Else branch of the If activity, and set the following values in the Properties panel.
Property Value Body
"Your order has been shipped. Thank you for shopping at contoso.com."
DisplayName
Send client notification
Subject
"Contoso.com Order#" + po.POID + "~~ Order Shipped"
To
po.EmailAddress
You have finished the workflow development.
In this configuration file, you define two endpoints and one behavior element.
To configure the Order Workflow service by using a configuration file
From Solution Explorer, expand OrderWorkflowService, and then double-click Web.config to open it.
Add a <services> tag inside the <system.serviceModel> tag.
<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>
To compile the Order Workflow service
- From Solution Explorer, right-click the OrderWorkflowService project, and then click Rebuild. Make sure the project is compiled successfully in the Output window.
Finishing the Order Processing service
OrderProcessingService and OrderWorkflowService reference each other. So you have to finish OrderProcessingService after finishing OrderWorkflowService.
To add the service references
From Solution Explorer, right-click OrderProcessingService, and then click Add Service Reference.
From Add Service Reference, click Discover. Visual Studio should discover both of the services.
Enter and select the following values, and then click OK to create the service reference.
Property Value Services
OrderWorkflow.xamlx
Namespace
OrderWorkflowService
To modify OrderProcess.svc
From Solution Explorer, expand OrderProcessingService, and then double-click OrderProcessing.svc to open it.
Uncomment the section inside the SendProcessResult function, so the function looks like:
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); } }
To compile the Order Processing service
- From Solution Explorer, right-click the OrderProcessingService project, and then click Rebuild. Make sure the project is compiled successfully in the Output window.
Finishing the Shipping service
ShippingService and OrderWorkflowService reference each other. So you have to finish ShipingService after finishing OrderWorkflowService.
To add the service references
From Solution Explorer, right-click ShippingService, and then click Add Service Reference.
From Add Service Reference, click Discover. Visual Studio should discover both of the services.
Enter and select the following values, and then click OK to create the service reference.
Property Value Services
OrderWorkflow.xamlx
Namespace
OrderWorkflowService
To modify Shipping.svc
From Solution Explorer, expand ShippingService, and then double-click Shipping.svc to open it.
Uncomment the section inside the SendShippingResult function, so the function looks like:
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); } }
To compile the Shipping service
- From Solution Explorer, right-click the ShippingService project, and then click Rebuild. Make sure the project is compiled successfully in the Output window.
Developing the Order Client Application
In this step, you develop a Windows Form client application.
To add a Windows Form application project to the solution
From Solution Explorer, right-click Solution ‘OrderService’, point to Add, and then click New Project.
From Add New Project, choose or type the following values, and then click OK.
Property Value Project types
Visual C#/Windows
Templates
Windows Forms Application
Name
OrderClient
Location
C:\DublinTutorial\OrderServiceSolution\OrderService
The Order client is an interface to the workflow service. You must add a service reference to the workflow service.
To add the service reference
From Solution Explorer, right-click OrderClient, and then click Add Service Reference to open the Add Service Reference dialog box.
From Add Service Reference, click Discover.
Enter and select the following values, and then click OK to create the service reference.
Property Value Services
OrderWorkflow.xamlx
Namespace
OrderWorkflowService
To develop the Windows Form
From Solution Explorer, expand OrderClient, and then double-click Form1.cs to open it.
Right-click Form1.cs, click Rename, and then type OrderForm.cs.
Click Yes on the prompt.
From the Toolbox, add the four Label controls, five TextBox controls, and three Button controls to the form, and align the controls so they look the following screen shot:
From the workflow, click the form, and set the following values in the Properties panel.
Property Value Name
OrderForm
Text
Contoso.com Order Form
From the workflow, click label1, and set the following values in the Properties panel.
Property Value Name
lblOrderNumber
Text
Order number:
From the workflow, click label2, and set the following values in the Properties panel.
Property Value Name
lblEmail
Text
Email:
From the workflow, click label3, and set the following values in the Properties panel.
Property Value Name
lblDescription
Text
Description:
From the workflow, click label4, and set the following values in the Properties panel.
Property Value Name
lblQuantity
Text
Quantity:
From the workflow, click textbox1, and set the following values in the Properties panel.
Property Value Name
txtOrderNumber
Enabled
False
From the workflow, click textbox2, and set the following values in the Properties panel.
Property Value Name
txtEmail
Text
JohnDole@fabrikam.com
From the workflow, click textbox3, and set the following values in the Properties panel.
Property Value Name
txtDescription
Text
Windows 7
From the workflow, click textbox4, and set the following values in the Properties panel.
Property Value Name
txtQuantity
Text
10
From the workflow, click textbox5, and set the following values in the Properties panel.
Property Value Name
txtStatus
Anchor
Bottom, Left, Right
Enabled
False
Text
“”
From the workflow, click button1, and set the following values in the Properties panel.
Property Value Name
btnSubmit
Anchor
Bottom, Right
Text
Submit
Click (Under the Event tab)
btnSubmit_Click
From the workflow, click button2, and set the following values in the Properties panel.
Property Value Name
btnUpdate
Anchor
Bottom, Right
Text
Update
Click (Under the Event tab)
btnUpdate_Click
From the workflow, click button3, and set the following values in the Properties panel.
Property Value Name
btnCancel
Anchor
Bottom, Right
Text
Cancel
Click (Under the Event tab)
btnCancel_Click
From Solution Explorer, right-click OrderForm.cs, and then click View Code.
Right-click the OrderClient namespace, click Refactor, and then click Rename to open the Rename dialog box.
From Rename, type Microsoft.Samples.Dublin.Tutorials.OrderService.OrderClient, and then click OK.
Click Apply.
Replace the code with the following:
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 } }
To compile the Order client
- From Solution Explorer, right-click the OrderClient project, and then click Rebuild. Make sure the project is compiled successfully in the Output window.
Testing the Order Service
To test the Order service
From Solution Explorer, right-click OrderClient, and then click Set as StartUp Project.
From Visual Studio, click the Debug menu, and then click Start Debugging. You should see a Windows Form opened.
From the form, click Submit.
Open Windows Explorer, and browse to the C:\DublinTutorial\Inbox folder.
Wait until you see all three e-mail notifications. It takes about three to four minutes to see all three files.
Packaging the Order Service
To package the OrderProcessingService WCF service
From Solution Explorer, right-click the OrderProcessingService project, and then click Package/Publish Settings.
Enter the following values:
Property Value Create web package as a ZIP file
(selected)
Location where the package will be created
C:\DublinTutorial\DeploymentPackages\OrderProcessingService.zip
IIS Web Site/Application name to be used on the destination server
OrderService/OrderProcessingService
From Solution Explorer, right-click the OrderProcessingService project, and then click Create Package.
Repeat the last procedure to create packages for the other three projects with the following settings:
Project name Location where the package will be created IIS Web Site/Application name to be used on the destination server OrderWorkflowService
C:\DublinTutorial\DeploymentPackages\OrderWorkflowService.zip
OrderService/OrderWorkflowService
ShippingService
C:\DublinTutorial\DeploymentPackages\ShippingService.zip
OrderService/ShippingService
Note
You might consider updating the endpoint addresses of the dependent services in the Web.config files to reflect the server where the packages will be deployed before packaging a service.
See Also
Concepts
Tutorial Using the Windows Server AppFabric Interface
Tutorial Using Windows PowerShell