Working with the .NET Framework 4.0 Windows Communication Foundation (WCF) Routing Service

Introduction

Often an integration solution requires some layer of indirection or mediation to handle things like security, versioning, and exceptions. Message Routing is a common pattern employed to handle such mediation. In .NET Framework 4.0, Windows Communication Foundation (WCF) now includes a Routing Service that encapsulates the Message Routing pattern.

Utilizing various routing service samples, this article will take a deep dive into the Routing Service showing how the Routing Service can be a solution for some common scenarios. Before delving into sample code some routing service architectural overview is in order.

Architectural Overview

A complete introduction to Routing Service and WCF architecture is beyond the scope of this article, so I'm going to center on where the Routing Service fits inside of WCF. If you've done work with WCF, you've probably seen the diagram below.

WCF routing service diagram
Figure 1: WCF components
Source

Recall that behaviors modify how the WCF Runtime behaves when a message travels out of the channel stack and the channel stack, composed of things like the binding, handle message transfer. Through Behaviors and accompanying extensibility points a developer can, for example, control how parts of WCF eventually invoke a proxy.

The routing service looks no different than any other WCF application. Since its operations are concerned with message contents and dispatching to proxies, it implements custom behaviors and WCF Runtime extensibility rather than a custom channel. Routing Service is composed of two parts: a Routing Service Type aptly named RoutingService and Service Behaviors applied to the host via a RoutingServiceConfiguration class.

With some context around where the routing service fits within WCF, looking at code will show how the routing service is used. Instead of creating an entirely new sample I'll walk through three SDK samples and explain how each sample works. First, I'll provide an overview of each sample and describe some common scenarios the pattern's applied in the sample relate to.

Samples Overview

There are a number of routing service samples among the WCF samples shipping with the SDK. You can download the sample here. The samples this article focuses on can be found in the "Basic\Routing Service" folder under the "WCF" samples folder. Here is a summary of each sample and a short description of the routing service functionality the sample demonstrates. All of the samples leverage the ubiquitous WCF Calculator Service.

  • HelloRoutingService - This sample simply demonstrates how to configure the routing service. In the sample the routing service serves as a layer of indirection that passes a message on to another service.
  • RouterBridgingandErrorHandling - As in the HelloRoutingService sample, this sample is a layer of indirection. The sample demonstrates two scenarios. The first is bridging protocols, exposing an HTTP binding to a client, but accessing a service using NetTcp binding. The second scenario is providing a failover EndPoint when the primary EndPoint fails.
  • AdvancedFilters - Like the samples above, this sample performs some mediation. The sample also leverages filter tables, examining the contents of a message and passing the message on to the appropriate service based on contents. Scenarios captured in the sample can apply to Service aggregation; exposing a set of services as a single EndPoint. Other scenarios covered by the sample include: service versioning and security.

In all the samples the RoutingService sits between the CalculatorService on the backend and the CalculatorClient. The samples contain configuration (app.config) and code configuration. I'll explain the samples from the code configuration perspective.

Now it's time for a deep dive into each sample.

HelloRoutingService

Below are key parts of code from the HelloRoutingService sample. As I stated earlier this sample shows how to wire up the routing service.

  using (ServiceHost serviceHost =
      new ServiceHost(typeof(RoutingService)))
  {
      string clientAddress = "http://localhost:8000/servicemodelsamples/service";
      string routerAddress = "http://localhost:8000/routingservice/router";
  
      Binding routerBinding = new WSHttpBinding();
      Binding clientBinding = new WSHttpBinding();
  
      serviceHost.AddServiceEndpoint(typeof(IRequestReplyRouter), routerBinding, routerAddress);
      ContractDescription contract = ContractDescription.GetContract(typeof(IRequestReplyRouter));
      ServiceEndpoint client = new ServiceEndpoint(contract, clientBinding, new EndpointAddress(clientAddress));
  
      RoutingConfiguration rc = new RoutingConfiguration();
      List<ServiceEndpoint> endpointList = new List<ServiceEndpoint>();
      endpointList.Add(client);
  
      rc.FilterTable.Add(new MatchAllMessageFilter(), endpointList);
  
      serviceHost.Description.Behaviors.Add(new RoutingBehavior(rc));
  
      serviceHost.Open();
      // The service can now be accessed.
      Console.ReadLine();
  
  }

There are primarily two parts to the routing service: the RoutingService class and some custom behaviors. Looking at how the RoutingService class is defined reveals some interesting facts. The RoutingService class definition appears below.

       [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
       [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any, InstanceContextMode = InstanceContextMode.PerSession, UseSynchronizationContext = false, ValidateMustUnderstand = false)]
       public sealed class RoutingService : ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter, IDuplexSessionRouter, IDisposable
 

The most interesting parts in the definition are that the ServiceBehavior turns off some default WCF validation and the InstanceContextMode setting. Deactivated automatic behavior is captured in the AddressFilterMode, UseSynchronixationContext, and ValidateMustUnderstand settings.

InstanceContextMode set to PerSession indicates that the proxy instance that is associated to the client invoking the service is maintained until the client no longer needs the service. Typically, PerSession instance context is required for services storing some sort of state associated to the client. A complete explanation of InstanceContextMode and how it impacts a service is beyond the scope of this article. The "Resources" section at the end of this article contains some more complete information.

Another interesting aspect of the class definition are the interfaces it implements. As you may have observed the interfaces are associated with the message exchange pattern implemented by a service EndPoint. In the example, the EndPoint is associated with IRequestReplyRouter interface.

RoutingConfiguration contains all the pertinent information for the RoutingBehavior class. Included in the RoutingConfiguration is a FilterTable containing a MatcheAllMessageFilter class. Later in the article I'll elaborate on the FilterTable, for now look at MatchAllMessageFilter as a sort of "pass-thru" for all Message classes.

HelloRoutingService contained the basics, a more advanced sample shows how the RoutingServices can perform protocol bridging and Failover.



Working with the .NET Framework 4.0 Windows Communication Foundation (WCF) Routing Service

RouterBridgingandErrorHandling

Much of the RouterBridgingandErrorHandling sample looks the same as the HelloRoutingService sample so the code appearing below captures the basic differences between the samples.

  string deadAddress = "net.tcp://localhost:9090/servicemodelsamples/fakeDestination";
  string realAddress = "net.tcp://localhost:8080/servicemodelsamples/service";
  string routerAddress = "http://localhost/routingservice/router";
  
  Binding routerBinding = new BasicHttpBinding();
  Binding clientBinding = new NetTcpBinding();
              
  serviceHost.AddServiceEndpoint(typeof(IRequestReplyRouter), routerBinding, routerAddress);
              
  ContractDescription contract = new ContractDescription("IRequestReplyRouter");
  ServiceEndpoint fakeDestination = new ServiceEndpoint(contract, clientBinding, new EndpointAddress(deadAddress));
  ServiceEndpoint realDestination = new ServiceEndpoint(contract, clientBinding, new EndpointAddress(realAddress));
              
  //create the endpoint list that contains the service endpoints we want to route to
  List<ServiceEndpoint> backupList = new List<ServiceEndpoint>();
  
  backupList.Add(fakeDestination);
  
  backupList.Add(realDestination);
              
  //create the default RoutingConfiguration option            
  RoutingConfiguration rc = new RoutingConfiguration();
              
  rc.FilterTable.Add(new MatchAllMessageFilter(), backupList);
              
  serviceHost.Description.Behaviors.Add(new RoutingBehavior(rc));

As I stated earlier, the RoutingService sits between the CalculatorService on the backend and the CalculatorClient application. You may have observed in the prior example that the CalculatorService and the CalculatorClient used the same binding. In this example the CalculatorService and the CalculatorClient use different bindings. The RoutingService documentation calls this protocol bridging. Note, however, that though the Bindings are different the message exchange pattern remains the same.

A noticeable difference between this example and the prior example is the additional EndPoint. According to the WCF documentation, when a MessageFilter signals a message match, the RoutingService will dispatch the message to the first EndPoint in the list of EndPoints attached to the filter. If the first EndPoint fails, the RoutingService will attempt to transmit to the next EndPoint in the list.

Thus far all the samples have been using the "Pass-thru" Message Filter called MatchAllMessageFilter, effectively bypassing the use of a message filter. The final sample tackles using message filters.

AdvancedFilters

Below are key parts of the AdvancedFilters sample. Most of the code common with the last two samples has been omitted. ...

...

  Binding routerBinding = new WSHttpBinding();
  Binding clientBinding = new NetTcpBinding();
  …
  ServiceEndpoint routerEndpoint = new ServiceEndpoint(contract, routerBinding, new EndpointAddress(routerAddress));
  routerEndpoint.Name = "routerEndpoint";
  
  ServiceEndpoint calcEndpoint = new ServiceEndpoint(contract, routerBinding, new EndpointAddress(virtualCalculatorAddress));
  calcEndpoint.Name = "calculatorEndpoint";
  
  ServiceEndpoint roundingEndpoint = new ServiceEndpoint(contract, routerBinding, new EndpointAddress(virtualRoundingCalculatorAddress));
  roundingEndpoint.Name = "roundingEndpoint";
  
  ServiceEndpoint RegularCalcEndpoint = new ServiceEndpoint(contract, new NetTcpBinding(), new EndpointAddress(calcDestinationAddress));
  ServiceEndpoint RoundingCalcEndpoint = new ServiceEndpoint(contract, new NetTcpBinding(), new EndpointAddress(roundingDestinationAddress));
              
  List<ServiceEndpoint> RegularCalcs = new List<ServiceEndpoint>();
  List<ServiceEndpoint> RoundingCalcs = new List<ServiceEndpoint>();
  
  RegularCalcs.Add(RegularCalcEndpoint);
  RoundingCalcs.Add(RoundingCalcEndpoint);
              
  RoutingConfiguration rc = new RoutingConfiguration();
  
  XPathMessageFilter("sm:header()/custom:RoundingCalculator = 1", namespaceManager);
  
  EndpointNameMessageFilter endpointNameFilter = new EndpointNameMessageFilter("calculatorEndpoint");
  
  PrefixEndpointAddressMessageFilter prefixAddressFilter = new PrefixEndpointAddressMessageFilter(new EndpointAddress("http://localhost/routingservice/router/rounding/"));
  
  RoundRobinMessageFilter roundRobinFilter1 = new RoundRobinMessageFilter("group1");
  RoundRobinMessageFilter roundRobinFilter2 = new RoundRobinMessageFilter("group1");
  rc.FilterTable.Add(xpathFilter, RoundingCalcs, 2);
  
  rc.FilterTable.Add(endpointNameFilter, RegularCalcs, 1);
  rc.FilterTable.Add(prefixAddressFilter, RoundingCalcs, 1);
  
  rc.FilterTable.Add(roundRobinFilter1, RegularCalcs, 0);
  rc.FilterTable.Add(roundRobinFilter2, RoundingCalcs, 0);
                          
  serviceHost.Description.Behaviors.Add(new RoutingBehavior(rc));

The sample code above leverages different types of MessageFilter classes. According to the WCF documentation MessageFilters (http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.messagefilter.aspx) allow a service to determine a course of action based on the contents of a message. In the RoutingService, MessageFilters direct the RoutingService to route an incoming Message to an outgoing EndPoint or collection of EndPoints. MessageFilter configuration in the AdvancedFilters sample is visually depicted below.

[Config.jpg]
Figure 2: AdvancedFilters MessageFilter layout

The sample utilizes many different types of MessageFilters. Some, like the XPathMessageFilter examine information in the message and others like the RoundRobinMessageFilter determine message routing using some other criteria. There are some important considerations to make when selecting a MessageFilter.

Multiple message filters may indicate a match. In this sample the first match will return the result. Multicast scenarios are possible, but an introduction to multicast scenarios is beyond the scope of this article.

For custom filters, like other parts of WCF, MessageFilter must obey rules around proper message handling, including buffering messages requiring some more detailed inspection. A complete introduction to Message handling is beyond the scope of this article, but you'll find good introductions under the Resources section of this article.

Finally, much of what the RoutingService does is driven by MessageFilters. All but trivial examples like the HelloRoutingService sample above should rely on MessageFilters.

Conclusion

Message Routing is a common Integration pattern. WCF captures the message routing pattern in new .NET framework 4.0 functionality called the routing service. Routing service provides support for integration scenarios requiring some layer of indirection. Scenario examples are service versioning, service aggregation, and security.

Resources

PDC 2009
Message Routing on Channel 9
Learn the ABCs of Programming Windows Communication Foundation
WCF Messaging Fundamentals
Extending WCF with Custom Behaviors
A Developer's Introduction to Windows Communication Foundation 4
Discover Mighty Instance Management Techniques for Developing WCF Apps
Enterprise Integration Patterns
Routing Message in WCF 4.0

Routing Scenarios

http://msdn.microsoft.com/en-us/library/ee816891.aspx
WCF 4.0 Routing Service Multicast Sample

Related Articles





About the Author

Jeffrey Juday

Jeff is a software developer specializing in enterprise application integration solutions utilizing BizTalk, SharePoint, WCF, WF, and SQL Server. Jeff has been developing software with Microsoft tools for more than 15 years in a variety of industries including: military, manufacturing, financial services, management consulting, and computer security. Jeff is a Microsoft BizTalk MVP. Jeff spends his spare time with his wife Sherrill and daughter Alexandra.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds