Your message has been sent.
This article has been saved to your account.
Go to my account
This article has been emailed to your Kindle.
Send this article
Complete the form below to send this article, Building the Content Based Routing Solution on Microsoft Platform, to a friend (or to yourself). We will never share your details (or your friend's) with anyone. For more information, read our Privacy Policy.
In the previous article, Content Based Routing on Microsoft Platform, we took a look at how to send data messages to the correct target system.
In this article by Richard Seroter, co-author of Applied Architecture Patterns on the Microsoft Platform, we will actually construct a working version of the proposed solution, which will leverage core components of .NET 4.0 (WCF and Windows Workflow Services) as well as the AppFabric extensions to IIS. Note that for this demonstration, we are only building the first aspect, which accepts orders, not the second piece which supports querying the status of a given order.
The flow of the solution looks like the following:

An order comes from a customer to a single endpoint at McKeever Technologies. This single endpoint then routes the order based on the content of the order (that is, the value of the Product ID element). The router sends requests to WCF Workflow Services, which can provide us durability and persistence when talking to the backend order management systems. If an order system is down, then the workflow gets suspended and will be capable of resuming once the system comes back online.
Setup
First, create a new database named Chapter8Db in your local SQL Server 2008 instance. Then locate the database script named Chapter8Db.sql in the folder <Installation Directory>\Chapter8\Begin and install the tables into your new database. When completed, your configuration should look like the following screenshot:

Next, open Chapter8.sln in the <Installation Directory>\Chapter8\Begin folder. In this base solution you will find two WCF services that represent the interfaces in front of the two order management systems at McKeever Technologies. Build the services and then add both of them as applications in IIS. Make sure you select the .NET 4.0 application pool.

If you choose, you can test these services using the WCF Test Client application that comes with the .NET 4.0 framework. If your service is configured correctly, an invocation of the service should result in a new record in the corresponding SQL Server database table.
Building the workflow
Now that our base infrastructure is in place, we can construct the workflows that will execute these order system services.
- Launch Visual Studio.NET 2010 and open Chapter8.sln in the <Installation Directory>\Chapter8\Begin folder. You should see two WCF services.

- We now must add a new workflow project to the solution. Recall that this workflow will sit in front of our order service and give us a stronger quality of service, thanks to the persistence capability of AppFabric. In Visual Studio .NET 2010, go to File and select New Project.
- Select the WCF Workflow Service project type under the Workflow category and add the project named Chapter8.SystemA.WorkflowService to our existing solution.

- This project is now part of the solution and has a default workflow named Service1.xamlx.

- Rename the Service1.xamlx file to SystemAOrderService.xamlx from within the Solution Explorer. Also click the whitespace within the workflow to change both the ConfigurationName and Name properties.

- We want all our service-fronting workflows to have the same external-facing contract interface so that we can effectively abstract the underlying service contracts or implementation nuances. Hence, we add a new class file named OrderDataContract.cs to this workflow project. This class will hold the data contracts defining the input and output for all workflows that sit in front of order systems.
- Make sure the project itself has a reference to System.Runtime.Serialization, and then add a using statement for System.Runtime.Serialization to the top of the OrderDataContract.cs class. Add the following code to the class:
namespace Chapter8.WorkflowService
{
[DataContract(
Namespace = "http://Chapter8/OrderManagement/DataContract")]
public class NewOrderRequest
{
[DataMember]
public string OrderId { get; set; }
[DataMember]
public string ProductId { get; set; }
[DataMember]
public string CustomerId { get; set; }
[DataMember]
public int Quantity { get; set; }
[DataMember]
public DateTime DateOrdered { get; set; }
[DataMember]
public string ContractId { get; set; }
[DataMember]
public string Status { get; set; }
}
[DataContract(
Namespace = "http://Chapter8/OrderManagement/DataContract")]
public class OrderAckResponse
{
[DataMember]
public string OrderId { get; set; }
}
} - Open the SystemAOrderService.xamlx workflow, click on the top ReceiveRequest shape, and set the following property values. Note that we will use the same values for all workflows so that the external-facing contract of each workflow appears the same.
Property Value DisplayName ReceiveOrderRequest OperationName SubmitOrder ServiceContractName {http://Chapter8/OrderManagement} ServiceContract Action http://Chapter8/OrderManagement/SubmitOrder CanCreateInstance True - Click the Variables tab at the bottom of the workflow designer to show the default variables added to the workflow.

- Delete the data variable.
- Create a new variable named OrderReq. For the variable type, choose Browse for Types and choose the NewOrderRequest type we defined earlier in the OrderDataContract.cs class.

- Add another variable named OrderResp and choose the previously defined OrderAckResponse .NET type.
- The OrderReq variable gets instantiated by the initial request, but we need to explicitly set the OrderResp variable. In the Default column within the Variables window, set the value to New OrderAckResponse().
- Set a proper variable for the initial receive shape by clicking on the ReceiveOrderRequest shape and click on the View Message link. Choose OrderReq as the Message data and set the type as NewOrderRequest.

- Now we do the same for the response shape. Select the SendResponse shape and click on the View Message link. Choose the OrderResp variable as the Message data and OrderAckResponse as the Message type.
- Keep the SendResponse shape selected and set its PersistBeforeSend property to On. This forces a persistence point into our workflow and ensures that any errors that occur later in the workflow will lead to a suspended/resumable instance.
- We can test our workflow service prior to completing it. We want to populate our service response object, so go to the Workflow toolbox, view the Primitives tab, and drag an Assign shape in between the existing receive and send shapes.

- In the Assign shape, set the To value to OrderResp.OrderID and the right side of the equation to System.GUID.NewGUID().ToString(). This sets the single attribute of our response node to a unique tracking value.
- Build the workflow and if no errors exist, right-click the SystemAOrderSystem.xamlx workflow in the Solution Explorer and choose View in Browser.
- Open the WCF Test Client application and point it to our Workflow Service endpoint. Double-click the Submit Order operation, select the datatype in the Value column, and enter test input data. Click on the Invoke button and observe the response object coming back with a GUID value returned in the OrderId attribute.

- Now we're ready to complete our workflow by actually calling our target WCF service that adds a record to the database table. Return to Visual Studio. NET, right-click the Chapter8.SystemA.WorkflowService project, and choose Add Service Reference.
- Point to the service located at http://localhost/Chapter8.OrderManagement.SystemA/OrderIntakeService.svc and type SystemASvcRef as the namespace.
- If the reference is successfully added and the project is rebuilt, then a new custom workflow activity should be added to the workflow toolbox. This activity encapsulates everything needed to invoke our system service.

- Add variables to the workflow that represent the input and output of our system service. Create a variable named ServiceRequest and browse for the type Order, which can be found under the service reference. Set the default value of this variable to New Order().

- Create another variable named ServiceResponse and pick the same order object but do not set a default value.
- Drag the custom AddOrder activity from the workflow toolbox and place it after the SendResponse shape. This sits after the workflow service response is sent, so that if errors occur the caller will not be impacted.
- Click the AddOrder shape and set its NewOrder property to the ServiceRequest variable and its AddOrderResult property to ServiceResponse.
- Now we have to populate the service request object. Drag a Sequence workflow activity from the Control Flow tab and drop it immediately before the AddOrder shape.
- Add six Assign shapes to the Sequence and set each activity's left and right fields as follows:
Left Side Right Side ServiceRequest.ContractId OrderReq.ContractId ServiceRequest.CustomerId OrderReq.CustomerId ServiceRequest.DateOrdered OrderReq.DateOrdered ServiceRequest.OrderNumber OrderResp.OrderId ServiceRequest.ProductId OrderReq.ProductId ServiceRequest.Quantity OrderReq.Quantity Note that the OrderNumber value of the request is set using the OrderResp object as that is the one to which we added the GUID value.
- Our final workflow should look like the following:

<protocolMapping>
<add scheme="http" binding="wsHttpBinding"/>
</protocolMapping>



(Move the mouse over the image to enlarge.)



<protocolMapping>
<add scheme="http" binding="wsHttpBinding"/>
</protocolMapping>
Adding a router service
Now we have two independent workflow services that sit in front of our order systems. We don't want our customers to know which service to call, but rather, we want them to send all their orders to one place and expect that McKeever Technologies will figure out a way to enter data accurately into appropriate systems.
WCF 4.0 introduces a pre-built routing service that uses configuration values to direct messages to endpoints, based on a variety of criteria. We can route messages based on their content, SOAP action, custom headers, and more. There's a great amount of flexibility we can add to our solution architecture when rich capabilities like message routing are simply baked into a framework.
The steps for building this part of the solution are as follows:
- In Visual Studio.NET, add a new website of type WCF Service to our existing solution and name it Chapter8.OrderManagement.RoutingService.

- Delete the code files (interface and service implementation). We are leveraging the native WCF 4.0 Routing Service and therefore don't need any code files.
- Add a project reference to System.ServiceModel.Routing.
- Rename the .svc file to OrderRouter.svc.
- Open the service file (.svc) and change its Service attribute reference to System.ServiceModel.Routing.RoutingService,System.ServiceModel.Routing, version=4.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35. This tells the WCF service to use the built-in router service for its implementation.
- We need to set up the web.config file to implement the routing capability. within the System.Configuration tags, add the <client> node. This holds the endpoint definition for both of our order system workflow services. Note that we don't identify the contract definition because this project technically has no idea about our service or its contract implementation.
<client>
<endpoint
address="http://localhost/Chapter8.SystemA.WorkflowService/
SystemAOrderService.xamlx" binding="wsHttpBinding"
bindingConfiguration="" contract="*" name="OrderSystemA" />
<endpoint
address="http://localhost/Chapter8.SystemB.WorkflowService/
SystemBOrderService.xamlx" binding="wsHttpBinding"
bindingConfiguration="" contract="*" name="OrderSystemB" />
</client> - We create the new WCF 4.0 routing section. We first have a namespace table, which allows us to create an alias to the namespace of our data message. Next, we have a filters collection where we have XPath filters for each order system and which sends messages with ProductId < 100 to system A and ProductId > 100 to system B. Finally, we have a filter table, which links the filters and determines which endpoint to use when the filter is satisfied.
<routing>
<namespaceTable>
<add prefix="custom"
namespace="http://Chapter8/OrderManagement/DataContract"/>
</namespaceTable>
<filters>
<filter name="SystemAFilter" filterType="XPath"
filterData="//custom:ProductId < '100'"/>
<filter name="SystemBFilter" filterType="XPath"
filterData="//custom:ProductId > '100'"/>
</filters>
<filterTables>
<filterTable name="filterTable1">
<add filterName="SystemAFilter" endpointName="OrderSystemA"
priority="0"/>
<add filterName="SystemBFilter" endpointName="OrderSystemB"
priority="0"/>
</filterTable>
</filterTables>
</routing> - We add a service and behavior entry. The behavior refers to the new routing capability, and the service points to the framework-provided routing service.
<services>
<service behaviorConfiguration="RoutingBehavior" name="System.
ServiceModel.Routing.RoutingService">
<endpoint address="" binding="wsHttpBinding"
bindingConfiguration=""
name="RouterEndpoint1" contract="System.ServiceModel.
Routing.IRequestReplyRouter" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="RoutingBehavior">
<routing routeOnHeadersOnly="false"
filterTableName="filterTable1" />
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors> - Build the application and create a new IIS application named Chapter8.OrderManagement.RoutingService, which runs in the .NET 4.0 application pool.
- We can browse directly to the service address to see if our service is online: http://localhost/Chapter8.OrderManagement.RoutingService/OrderRouter.svc.
- If you browse the WSDL of this service, you will notice that it has a generic request-reply message exchange pattern. Clients of this service should point to a well-defined WSDL that outlines the specific service and data contracts.
- To test this service, create a new console application and add a service reference to either of our previously built workflow services. Add the following code to the Main operation:
static void Main(string[] args)
{
Console.WriteLine("Starting Up Service Client ...");
Console.WriteLine("Enter a product to order");
string prodId = Console.ReadLine();
OrderSvcRef.ServiceContractClient c = new
OrderSvcRef.ServiceContractClient
("wsHttpBinding_
ServiceContract");
OrderSvcRef.NewOrderRequest request = new
OrderSvcRef.
NewOrderRequest();
request.ContractId = "001";
request.CustomerId = "333";
request.DateOrdered = DateTime.Now;
request.ProductId = prodId;
request.Quantity = 10;
request.Status = "Submitted";
OrderSvcRef.OrderAckResponse response = c.SubmitOrder(request);
Console.WriteLine("Response is " + response.OrderId);
Console.ReadLine();
} - Open the app.config file for the project and find the endpoint added by the service reference. Remove the URL to the specific workflow service and replace it with the generic router address. http://localhost/Chapter8.OrderManagement.RoutingService/OrderRouter.svc.
- Build and run the console application and if you enter a product ID below 100, you should see a record added to the SystemA database table, and conversely, if you enter a product ID greater than 100, you should find a new record in the SystemB database table.
Summary
In this article we constructed a working version of the proposed solution, which leveraged core components of .NET 4.0 (WCF and Windows Workflow Services) as well as the AppFabric extensions to IIS.
About the Author :
Richard Seroter
Richard Seroter is a solutions architect for an industry-leading biotechnology company, a Microsoft MVP for BizTalk Server, and a Microsoft Connected Systems Advisor. He has spent the majority of his career consulting with customers as they planned and implemented their enterprise software solutions. Richard worked first for two global IT consulting firms, which gave him exposure to a diverse range of industries, technologies, and business challenges. Richard then joined Microsoft as a SOA/BPM technology specialist where his sole objective was to educate and collaborate with customers as they considered, designed, and architected BizTalk solutions. One of those customers liked him enough to bring him onboard full time as an architect after they committed to using BizTalk Server as their enterprise service bus. Once the BizTalk environment was successfully established, Richard transitioned into a solutions architect role where he now helps identify enterprise best practices and applies good architectural principles to a wide set of IT initiatives.
Richard maintains a semi-popular blog of his exploits, pitfalls, and musings with BizTalk Server and enterprise architecture at http://seroter.wordpress.com.
The authors have provided a website with further information about the book here: http://appliedarchitecturepatterns.com/
Books From Packt
|
|
|



Post new comment