Practical .NET

Configuring WCF Routing Without Code

Peter Vogel returns to creating a Windows Communication Foundation (WCF) router that loosely couple services with their clients. But this time he uses XML to configure his router rather than doing it in code.

Way back in August of last year, I encouraged you to add a router to your Windows Communication Foundation (WCF) services as a way of loosely coupling services to their consumers. In that article, I walked through the process of setting up a WCF service router that would accept requests and, based on criteria applied to that request, reroute the request to the service that would best handle the request. It's a neat feature of WCF and enables you, for instance, to enhance or replace services without forcing consumers to be recompiled (or, at any rate, to have to use new endpoints). It also lets you route a single request to multiple services for processing.

Last week I was teaching Learning Tree's WCF course (which also now covers the Web API) and when we got to the section on service routing, I commented that I didn't know how to configure routing using the system.serviceModel elements in the config file -- I only knew how to do it from code. One of the course participants put together the equivalent XML and sent it to me. (Thanks, Greg!) This column shows off that XML and lets me describe how to a create router in Visual Basic (I did the original column in C#).

Setting up the Routing Host
To create a WCF router, you first create a WCF routing host (I've used a console application here to facilitate testing but, in real life, you'd put this code in the OnStart method of a Windows Service). You'll need to add references to the System.ServiceModel and System.ServiceModel.Routing libraries to your project and then put these Imports in your code file:

Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports System.ServiceModel.Dispatcher
Imports System.ServiceModel.Routing

With that in place (and if you're going to do all of your configuration in XML), the only lines of code you need to create a routing host are these:

Dim host As New ServiceHost(GetType(RoutingService))
host.Open

Because I'm using a Console application, I also need a Console.ReadLine to hold the Console application in memory:

Console.ReadLine

The next step is to add Service References to the WCF Service Library or WCF Service Application projects holding the services to which you want to route requests. That will create a config file in your routing host project (if you don't have one already) and add the XML that references the services to which you just added a reference (or references). You need to tweak that XML by setting the contract attribute to * (and make a note of the name that's been assigned to the endpoint):

<client>
  <endpoint ...
            contract="*"
            name="MyRoutedService">
  </endpoint>
</client>

Configuring Routing in XML
Now you need to add to that config file the XML that makes your host a routing host. Your routing host is itself a service that accepts incoming requests, so you start by defining that with service and endpoint elements inside a services element right after the existing System.ServiceModel element. Your service must be named System.ServiceModel.Routing.RoutingService, and the endpoint contract must be System.ServiceModelRouting.IRequestReplyRouter. You'll also need to assign the service a behaviorConfiguration, a binding type and an endpoint (you can use whatever makes sense to you in these entries).

This example sets up the routing host to accept requests using wsHttpBinding at a URL on a server called MyServer with a behaviorConfiguration I've called MyRoutingConfiguration (I use the baseAddresses element to specify the host's endpoint so that I can use the serviceMetadata element in my behavior):

<services>
  <service behaviorConfiguration="MyRoutingConfiguration"
           name="System.ServiceModel.Routing.RoutingService">
   <endpoint address=""
             binding="wsHttpBinding"
             contract="System.ServiceModel.Routing.IRequestReplyRouter"/>
   <host>
     <baseAddresses>
       <add baseAddress="http://localhost:10002/router"/>
     </baseAddresses>
   </host> 
  <service>
</services>

Now you need to add the serviceBehavior to your XML behaviors element using the behaviorConfiguration name you specified. All that's necessary in this element is to point to a filter table that you'll define later in your XML. I included the serviceMetadata and serviceDebug elements to support testing and debugging, and will remove them before moving to production:

<behaviors>
  <serviceBehaviors>
    <behavior name="MyRoutingConfiguration">
      <routing filterTableName="MyFilterTable"/>
      <serviceMetadata httpGetEnabled="True"/>
      <serviceDebug includeExceptionDetailInFaults="False" />
    </behavior>
  ...
    <client>

Now it's time to define the filter table. That's done inside a routing element, which you can put at the end of your existing XML just before the closing the system.ServiceModel element:

  <routing>
  </routing>
</system.ServiceModel>

Inside the routing element, first define the filters you're going to use in the filter table to identify incoming requests. In my previous column, I used two ActionMessageFilter objects: a MatchAll filter that matches any request and an ActionFilter that matches requests using the namespace for a service and the name of a method in that service. This XML sets up the equivalent filters in XML (you can give your filters any name you want):

<routing>
  <filters>
    <filter name="AllRequests" filterType="MatchAll"/>
    <filter name="RouteGetCustomerByID" filterType="Action" 
      filterData="http://www.phvis.com/customermanagement/ICustomerService/GetCustomerById"/>
  </filters>

Once you've set up all the filters you need, you can use them in your filter table, assigning the filter table the name you used in your behavior element (put this after the closing tag for the filters element):

<filterTables>
  <filterTable name="MyFilterTable">
  </filterTable>

Within your filter table, you use the add element to bring together one of your filters with the service that you want that filter to send any matching requests to. This example uses the Action filter I established to route to the service added at the start of this column (put this inside the filterTable element):

<add filterName="RouteGetCustomerByID" endpointName="MyRoutedService"/>

You can also use a priority element on the add element to control the order in which filters are processed.

Now, when you create a client, after adding a reference to your service to the client, make sure that you change the service endpoint address in the client to the address for your routing host. To test this application you might also need to run the netsh utility (see the end of my earlier article for how to do that).

It's up to you whether you use code or XML to configure your router host. In Visual Studio 2010 and earlier, you'll get more IntelliSense support with code than with XML (in Visual Studio 2012, they're about equal). Using XML lets you reconfigure your service on the site without recompiling it just by replacing the config file. I have to admit that the reason I like the XML way of doing things is because I can look at the config file in production and be sure about the configuration of the service -- something I can't be as sure of when the configuration is done in code. Let's just say that some of my client's source control and release practices aren't as … dependable … as I might hope.

About the Author

Peter Vogel is a principal in PH&V Information Services, specializing in Web development with expertise in SOA, client-side development, and user interface design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His most recent book ("rtfm*") is on writing effective user manuals, and his blog posts on communicating effectively can be found at http://blog.learningtree.com/category/communication-2/.

comments powered by Disqus
Upcoming Events

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.