Hosting the WCF 4.0 Routing Service in IIS 7

I recently had occasion to explore the new WCF 4.0 Routing Service and thought I’d share how I set up a simple solution that demonstrated its capabilities and highlights how to host it within IIS.

[UPDATE: I’ve got a simpler way to do this in a later post that you can find here.]

This new built-in service allows us to put a simple broker in front of our services and route inbound messages based on content, headers, and more.  Problem for me was that every demo I’ve seen of this thing (from PDC, and other places) show simple console hosts for the service and not a more realistic web server host.  This is where I come in.

First off, I need to construct the services that will be fronted by the Routing Service.  In this simple case, I have two services that implement the same contract.  In essence, these services take a name and gender, and spit back the appropriate “hello.”  The service and data contracts look like this:

[ServiceContract]
    public interface IHelloService
    {
        [OperationContract]
        string SayHello(Person p);
    }

    [DataContract]
    public class Person
    {
        private string name;
        private string gender;

        [DataMember]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        [DataMember]
        public string Gender
        {
            get { return gender; }
            set { gender = value; }
        }
    }

I then have a “HelloServiceMan” service and “HelloServiceWoman” service which implement this contract.

public class HelloServiceMan : IHelloService
    {

        public string SayHello(Person p)
        {
            return "Hey Mr. " + p.Name;
        }
    }

I’ve leveraged the new default binding capabilities in WCF 4.0 and left my web.config file virtually empty.  After deploying these services to IIS 7.0, I can use the WCF Test Client to prove that the service performs as expected.

2009.12.16router01

Nice.  So now I can add the Routing Service.  Now, what initially perplexed me is that since the Routing Service is self contained, you don’t really have a *.svc file, but then I didn’t know how to build a web project that could host the service.  Thanks to Stephen Thomas (who got code from the great Christian Weyer) I got things working.

You need three total components to get this going.  First, I created a new, Empty ASP.NET Web Application project and added a .NET class file.  This class defines a new ServiceHostFactory class that the Routing Service will use.  That class looks like this:

class CustomServiceHostFactory : ServiceHostFactory
{
    protected override System.ServiceModel.ServiceHost CreateServiceHost(System.Type serviceType, System.Uri[] baseAddresses)
    {
        var host = base.CreateServiceHost(serviceType, baseAddresses);

        var aspnet = host.Description.Behaviors.Find<AspNetCompatibilityRequirementsAttribute>();

        if (aspnet == null)
        {
            aspnet = new AspNetCompatibilityRequirementsAttribute();
            host.Description.Behaviors.Add(aspnet);
        }

        aspnet.RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed;

        return host;
    }
}

Here comes the tricky, but totally logical part.  How do you get the WCF Routing Service instantiated?  Add a global.asax file to the project and add the following code to the Application_Start method:

using System.ServiceModel.Activation;
using System.ServiceModel.Routing;
using System.Web.Routing;

namespace WebRoutingService
{
    public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.Add(
               new ServiceRoute("router", new CustomServiceHostFactory(),
                   typeof(RoutingService)));
        }

Here we stand up the Routing Service with a “router” URL extension.  Nice.  The final piece is the web.config file.  Here is where you actually define the Routing Service relationships and filters.  Within the system.serviceModel tags, I defined my client endpoints that the router can call.

<client>
      <endpoint address="http://localhost/FirstWcfService/HelloServiceMan.svc"
          binding="basicHttpBinding" bindingConfiguration="" contract="*"
          name="HelloMan" />
      <endpoint address="http://localhost/FirstWcfService/HelloServiceWoman.svc"
          binding="basicHttpBinding" bindingConfiguration="" contract="*"
          name="HelloWoman" />
    </client>

The Routing Service ASP.NET project does NOT have any references to the actual endpoint services, and you can see here that I ignore the implementation contract.  The router knows as little as possible about the actual endpoints besides the binding and address.

Next we have the brand new “routing” configuration type which identifies the filters used to route the service messages.

<routing>
      <namespaceTable>
        <add prefix="custom" namespace="http://schemas.datacontract.org/2004/07/FirstWcfService"/>
      </namespaceTable>
      <filters>
        <filter name="ManFilter" filterType="XPath" filterData="//custom:Gender = 'Male'"/>
        <filter name="WomanFilter" filterType="XPath" filterData="//custom:Gender = 'Female'"/>
      </filters>
      <filterTables>
        <filterTable name="filterTable1">
          <add filterName="ManFilter" endpointName="HelloMan" priority="0"/>
          <add filterName="WomanFilter" endpointName="HelloWoman" priority="0"/>
        </filterTable>
      </filterTables>
    </routing>

I first added a namespace prefix table, then have a filter collection which, in this case, uses XPath against the inbound message to determine the gender value within the request.  Note that if you want to use a comparison operation such as “<” or “>”, you’ll have to escape it in this string to “&gt;” or “&lt;”.  Finally, I have a filter table which maps a particular filter to which endpoint should be applied.

Finally, I have the service definition and behavior definition.  These both leverage objects and configuration items new to WCF 4.0.  Notice that I’m using the “IRequestReplyRouter” contract since I have a request/reply service being fronted by the Routing Service.

<services>
      <service behaviorConfiguration="RoutingBehavior" name="System.ServiceModel.Routing.RoutingService">
        <endpoint address="" binding="basicHttpBinding" 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>

Once we build and deploy the service to IIS 7, we can browse it.  Recall that in our global.asax file we defined a URL suffix named “router.”  So, to hit the service, we load our web application and append “router.”

2009.12.16router02

As you’d expect, this WSDL tells us virtually nothing about what data this service accepts.  What you can do from this point is build a service client which points at one of the actual services (e.g. “HelloServiceMan”), but then switch the URL address in the application’s configuration file.  This way, you can still import all the necessary contract definitions, while then switching to leverage the content-based routing service.

So, the Routing Service is pretty cool.  It does a light-weight version of what BizTalk does for routing.  I haven’t played with composite filters and don’t even know if it’s possible to have multiple filter criteria (like you can with a BizTalk Server subscription).  Either way, it’s good to know how to actually deploy this new capability in an enterprise web server instead of a console host.

Anyone else have lessons learned with the Routing Service?

Share

Author: Richard Seroter

Richard Seroter is Director of Developer Relations and Outbound Product Management at Google Cloud. He’s also an instructor at Pluralsight, a frequent public speaker, the author of multiple books on software design and development, and a former InfoQ.com editor plus former 12-time Microsoft MVP for cloud. As Director of Developer Relations and Outbound Product Management, Richard leads an organization of Google Cloud developer advocates, engineers, platform builders, and outbound product managers that help customers find success in their cloud journey. Richard maintains a regularly updated blog on topics of architecture and solution design and can be found on Twitter as @rseroter.

5 thoughts

  1. THanks for a very useful article. I tried the example in your article recently and could not get it to work. Can you post details on how you created your client and how you tested your service?

    1. I actually used the WCF Test Client that comes with the .NET Framework installation. I pointed at a service with the contract that expressed the actual payload, and then right-clicked the service in the Test Client, updated the endpoint to the Router URL, and sent the message along.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.