SIMPLER Way of Hosting the WCF 4.0 Routing Service in IIS7

A few months back I was screwing around with the WCF Routing Service and trying something besides the “Hello World” demos that always used self-hosted versions of this new .NET 4.0 WCF capability. In my earlier post, I showed how to get the Routing Service hosted in IIS.  However, I did it in a round-about way since that was only way I could get it working.  Well, I have since learned how to do this the EASY way, and figured that I’d share that. As a quick refresher, the WCF Routing Service is a new feature that provides a very simple front end service broker which accepts inbound messages and distributes them to particular endpoints based on specific filter criteria.  It leverages your standard content-based routing pattern, and is not a pub/sub mechanism.  Rather, it should be used when you want to send an inbound message to one of many possible destination endpoints. I’ll walk through a full solution scenario here.  We start with a standard WCF contract that will be shared across the services sitting behind the Router service.  Now you don’t HAVE to use the same contract for your services, but if not, you’ll need to transform the content into the format expected by each downstream service, or, simply accept untyped content into the service.  Your choice.  For this scenario, I’m using the Routing Service to accept ticket orders, and based on the type of event that the ticket applies to, routes it to the right ticket reservation system.  My common contract looks like this:

[ServiceContract]
    public interface ITicket
    {
        [OperationContract]
        string BuyTicket(TicketOrder order);
    }

    [DataContract]
    public class TicketOrder
    {
        [DataMember]
        public string EventId { get; set; }
        [DataMember]
        public string EventType { get; set; }
        [DataMember]
        public int CustomerId { get; set; }
        [DataMember]
        public string PaymentMethod { get; set; }
        [DataMember]
        public int Quantity { get; set; }
        [DataMember]
        public decimal Discount { get; set; }
    }

I then added two WCF Service web projects to my solution.  They each reference the library holding the previously defined contract, and implement the logic associated with their particular ticket type.  Nothing earth-rattling here:

public string BuyTicket(TicketOrder order)
    {
        return "Sports - " + System.Guid.NewGuid().ToString();
    }

I did not touch the web.config files of either service and am leveraging the WCF 4.0 capability to have simplified configuration. This means that if you don’t add anything to your web.config, some default behaviors and bindings are used. I then deployed each service to my IIS 7 environment and tested each one using the handy WCF Test Client tool.  As I would hope for, calling my service yields the expected result: 2010.3.9router01 Ok, so now I have two distinct services which add orders for a particular type of event.  Now, I want to expose a single external endpoint by which systems can place orders.  I don’t want my service consumers to have to know my back end order processing system URLs, and would rather they have a single abstract endpoint which acts as a broker and routes messages around to their appropriate target. So, I created a new WCF Service web application.  At this point, just for reference I have four projects in my solution. 2010.3.9router02 Alrighty then.  First off, I removed the interface and service implementation files that automatically get added as part of this project type.  We don’t need them.  We are going to reference the existing service type (Routing Service) provided by WCF 4.0.  Next, I went into the .svc file and changed the directive to point to the FULLY QUALIFIED path of the Routing Service.  I didn’t capitalize those words in the last sentence just because I wanted to be annoying, but rather, because this is what threw me off when I first tried this back in December.

<%@ ServiceHost Language="C#" Debug="true" Service="System.ServiceModel.Routing.RoutingService,System.ServiceModel.Routing, version=4.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35"  %>

Now all that’s left is the web.config file.  The configuration file needs a reference to our service, a particular behavior, and the Router specific settings. I first added my client endpoints:


Then I added the new “routing” configuration section.  Here I created a namespace alias and then set each Xpath filter based on the “EventType” node in the inbound message.  Finally, I linked the filter to the appropriate endpoint that will be called based on a matched filter.


After that, I added a new WCF behavior which leverages the “routing” behavior and points to our new filter table.


Finally, I’ve got my service entry which uses the above behavior and defines which contract we wish to use.  In my case, I have request/reply operations, so I leveraged the corresponding contract in the Routing service.


After deploying the routing service project to IIS, we’re ready to test.  What’s the easiest way to test this bad boy?  Well, we can take our previous WCF Test Client entry, and edit it’s WCF configuration.  This way, we get the strong typing on the data entry, but ACTUALLY point to the Routing service URL. 2010.3.9router03 After the change is made, we can view the Configuration file associated with the WCF Test Client and see that our endpoint now refers to the Routing service. 2010.3.9router04 Coolio.  Now, we can test.  So I invoked the BuyTickets operation and first entered a “Sports” type ticket. 2010.3.9router05 Then, ALL I did was switch the EventType from “Sports” to “Concert” and the Routing service should now call the service which fronts the concert reservation service. 2010.3.9router06 There you have it.  What’s nice here, is that if I added a new type of ticket to order, I could simply add a new back end service, update my Routing service filter table, and my service consumers don’t have to make a single change.  Ah, the power of loose coupling. You all put up with these types of posts from me even though I almost never share my source code.  Well, your patience has paid off.  You can grab the full source of the project here.  Knock yourselves out. Share

Author: Richard Seroter

Richard Seroter is currently the Chief Evangelist at Google Cloud and leads the Developer Relations program. 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 Chief Evangelist at Google Cloud, Richard leads the team of developer advocates, developer engineers, outbound product managers, and technical writers who ensure that people find, use, and enjoy Google Cloud. Richard maintains a regularly updated blog on topics of architecture and solution design and can be found on Twitter as @rseroter.

16 thoughts

  1. Thanks Richard for the post.

    Based on your experience, how would you go about impersonating the caller in the back-end services ? Usually, you have to define your SPN’s and make changes to the config file. Would it work the same using the routing service ?

    Chris

  2. Richard,

    In this example, you had to leave the .svc extension in the URL to your service (if I read that right). I’m trying to host a Rest service (webHttpBinding) in IIS (and under AppFabric), but part of the URL will be dynamic and I’d like to eliminate the suffix. Your December post showed one way to do this, but I’m not sure where the ServiceRoute type came from. Any pointers? Thanks for the post,

    -Erik

  3. Thanks for posting this. One question: why couldn’t this be used for a pub-sub scenario? I suppose the routing service is not totally agnostic of the subscribers, so it isn’t strictly speaking pub-sub…but it could easily be used in that way to multicast messages to multiple services that can accept the same datacontract, right?

  4. Hi Dave,

    I THINK that you only specify single subscribers to a filter. So, it’s meant for pure CBR where you have data fulfilled by a single endpoint. That said, I could be wrong, and maybe there’s a way to have a filter apply to many endpoints. Guess I’ll have to try that 😉

  5. Good find, Dave. I’ll have to look at the code and see if indeed, multiple endpoints have an identical filter. I also wonder if the endpoints are called sequentially, or on multiple threads.

  6. The xpath filters shown aren’t working as shown. The ‘No Matching MessageFilter was found for the given Message’ is returned as an exception.

    Modifying the filters to this worked though:

  7. The filterData fields are shown here:

    filterData=”boolean(//*[local-name()= ‘EventType’]/text() = ‘Concerts’)”
    filterData=”boolean(//*[local-name()= ‘EventType’]/text() = ‘Sports’)”

      1. I’m also encountering the same problem as Utekai, can someone please help? I’ve tried everything to no avail.

Leave a comment

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