Click here to Skip to main content
Click here to Skip to main content
Go to top

Content Based Routing Using WCF 4

, 28 Jul 2010
Rate this:
Please Sign up or sign in to vote.
This article explains how to do content based routing using the WCF 4 RoutingService.

Introduction

This article describes content based routing using WCF. Content based routing fulfills one of the four tenets of SOA which states that schemas and contracts can be shared, not classes. In message oriented applications, the client needs to only bother about the message it needs to submit and one service which would take care of its routing, i.e., submitting the message to one of the appropriate downstream WCF services. Thus, to the outside world, only the routing services and the contracts are exposed. This alleviates the need of point to point communication, and makes WCF behave more or less like a service bus.

Background

A background knowledge of C# 4.0, WCF 4.0, and ASP.NET are required to comprehend the article properly. A basic theoretical understanding of SOA and ESB are desirable.

Using the Code

The following steps are to be completed in order to realize content based routing using WCF 4.0:

  1. Create a blank Visual Studio 2010 solution named ContentBasedRouting.
  2. Add a Class Library project called CustomerContract.
  3. Rename the Class1.cs file to ICustomer.cs.
  4. Put the following code in it:
  5. using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Web;
    using System.Runtime.Serialization;
    
    namespace CustomerContract
    {
        [ServiceContract]
        public interface ICustomer
        {
            [OperationContract]
            string GetCustomerDetails(Customer cust);
        }
        [DataContract]
        public class Customer
        {
            [DataMember]
            public string CustomerID { get; set; }
            [DataMember]
            public string CustomerName { get; set; }
            [DataMember]
            public string CustomerCreditRating { get; set; }
        }
    }

    Here, we declare a ServiceContract, an interface ICustomer with the OperationContract as GetCustomerDetails taking a Customer object as parameter. We declare a DataContract, and a class Customer with three DataMembers as automatic properties.

  6. Next, we add references to System.ServiceModel.dll, System.ServiceModel.Description.dll, System.Runtime.Serialization.dll, and System.ServiceModel.Web.dll.
  7. Add a WCF service application called PremiumCustomerService to the solution. Also create a virtual directory in IIS 7.0 with physical path pointing to the PremiumCustomerService folder.
  8. Rename the SVC file to PremiumCustomerService.svc. It should contain the following code:
  9. <%@ ServiceHost Language="C#" Debug="true" 
        Service="PremiumCustomerService.PremiumCustomerService" 
        CodeBehind="PremiumCustomerService.svc.cs" %>
  10. Go to the code-behind file and add the following code:
  11. using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.Text;
    
    namespace PremiumCustomerService
    {
        // NOTE: You can use the "Rename" command on the "Refactor" menu
        // to change the class name "Service1"
        // in code, svc and config file together.
        public class PremiumCustomerService : CustomerContract.ICustomer
        {
            public string GetCustomerDetails(CustomerContract.Customer cust)
            {
                return "Customer ID = " + cust.CustomerID + 
                       " Premium CustomerName = " + 
                       cust.CustomerName + " CustomerCreditRating = " + 
                       cust.CustomerCreditRating;
            }
        }
    }

    Here we add a class PremiumCustomerService which implements the interface ICustomer, subsequently implementing the method GetCustomerDetails().

  12. Next, the web.config file has to be modified. The following should be the contents of the web.config file:
  13. <?xml version="1.0"?>
    <configuration>
      <system.web>
        <compilation debug="true" targetFramework="4.0" />
      </system.web>
      <system.serviceModel>
        <bindings>
          <netTcpBinding>
            <binding name="NetTcpBinding_ICustomer" portSharingEnabled="true">
              <security mode="None" />
            </binding>
          </netTcpBinding>
          <basicHttpBinding>
            <binding name="BasicHttpBinding_ICustomer">
              <security mode="None"/>
            </binding>
          </basicHttpBinding>
        </bindings>
        <services>
          <service name="PremiumCustomerService">
            <endpoint address="PremiumCustomerService.svc" binding="basicHttpBinding" 
               bindingConfiguration="BasicHttpBinding_ICustomer" 
               contract="CustomerContract.ICustomer"/>
            <endpoint address="mex" binding="mexHttpBinding" 
               contract="IMetadataExchange" />
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost/PremiumCustomerService/" />
              </baseAddresses>
            </host>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <!-- To avoid disclosing metadata information, set the value below 
                 to false and remove the metadata endpoint above before deployment -->
              <serviceMetadata httpGetEnabled="true"/>
              <!-- To receive exception details in faults for debugging purposes, 
                 set the value below to true. Set to false before deployment 
                 to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
     <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
     </system.webServer> 
    </configuration>

    Here we have PremiumCustomerService defined with basicHttpBinding. Now the service is ready. At this point, compile and build the solution. Now we can browse the service from IIS manager and see the URL as http://localhost/PremiumCustomerService/PremiumCustomerService.svc.

  14. Now add another WCF service application named OrdinaryCustomerService to the solution.
  15. Rename the .svc file to OrdinaryCustomerService.svc. The contents of the file should be the following:
  16. <%@ ServiceHost Language="C#" Debug="true" 
        Service="OrdinaryCustomerService.OrdinaryCustomerService" 
        CodeBehind="OrdinaryCustomerService.svc.cs" %>
  17. Modify the code-behind to contain the following code:
  18. using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.Text;
    
    namespace OrdinaryCustomerService
    {
        // NOTE: You can use the "Rename" command on the "Refactor"
        // menu to change the class name "Service1"
        // in code, svc and config file together.
        public class OrdinaryCustomerService : CustomerContract.ICustomer
        {
            public string GetCustomerDetails(CustomerContract.Customer cust)
            {
                return "Customer ID = " + cust.CustomerID + 
                       " Ordinary CustomerName = " + 
                       cust.CustomerName + " CustomerCreditRating = " + 
                       cust.CustomerCreditRating;
            }
        }
    }

    Here we add a class called OrdinaryCustomerService which implements the ICustomer interface and hence the method GetCustomerDetails().

  19. Next, the corresponding web.config file should be modified to contain the following:
  20. <?xml version="1.0"?>
    <configuration>
      <system.web>
        <compilation debug="true" targetFramework="4.0" />
      </system.web>
      <system.serviceModel>
        <bindings>
          <netTcpBinding>
            <binding name="NetTcpBinding_ICustomer" portSharingEnabled="true">
              <security mode="None" />
            </binding>
          </netTcpBinding>
          <basicHttpBinding>
            <binding name="BasicHttpBinding_ICustomer">
              <security mode="None"/>
            </binding>
          </basicHttpBinding>
        </bindings>
        <services>
          <service name="OrdinaryCustomerService">
            <endpoint address="OrdinaryCustomerService.svc" binding="basicHttpBinding" 
               bindingConfiguration="BasicHttpBinding_ICustomer" 
               contract="CustomerContract.ICustomer"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost/OrdinaryCustomerService/" />
              </baseAddresses>
            </host>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <!-- To avoid disclosing metadata information, set the value below 
                  to false and remove the metadata endpoint above before deployment -->
              <serviceMetadata httpGetEnabled="true"/>
              <!-- To receive exception details in faults for debugging purposes, 
                  set the value below to true. Set to false before deployment 
                  to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
     <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
     </system.webServer>
    </configuration>

    We have completed another service called OrdinaryCustomerService. At this point, compile and build the solution again. In IIS Manager, we can browse the service to see that the URL is http://localhost/OrdinaryCustomerService/OrdinaryCustomerService.svc.

  21. Add another WCF service application called CustomerService to the solution. Now this is where we would define our routing service, filters, and XPath expressions.
  22. Add a reference to System.ServiceModel.Routing.dll.
  23. The contents of CustomerService.svc should be the following:
  24. <%@ ServiceHost Language="C#" Debug="true" 
        Service="System.ServiceModel.Routing.RoutingService,System.ServiceModel.Routing, 
                 version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

    Here we are using the System.ServiceModel.Routing.RoutingService class as the service. This class is predefined in the System.ServiceModel.Routing.dll assembly. We will compile and build the solution again.

  25. Now we present the most important thing, i.e., the web.config file for the routing service. Here we define a filterTable for the service behavior and add filters and XPath expressions under the routing element. The services are defined under the client section of web.config. The contents of web.config should be like the following:
  26. <?xml version="1.0"?>
    <configuration>
      <system.web>
        <compilation debug="true" targetFramework="4.0" />
      </system.web>
      <system.serviceModel>
        <bindings>
          <netTcpBinding>
            <binding name="NetTcpBinding_ICustomer" portSharingEnabled="true">
              <security mode="None" />
            </binding>
          </netTcpBinding>
          <basicHttpBinding>
            <binding name="BasicHttpBinding_ICustomer">
              <security mode="None"/>
            </binding>
          </basicHttpBinding>
        </bindings>
        <client>
          <endpoint 
            address="http://localhost/PremiumCustomerService/PremiumCustomerService.svc" 
            binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ICustomer" 
            contract="*" name="CustomerServiceLibrary_PremiumCustomerService"/>
          <endpoint 
            address="http://localhost/OrdinaryCustomerService/OrdinaryCustomerService.svc" 
            binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ICustomer" 
            contract="*" name="CustomerServiceLibrary_OrdinaryCustomerService"/>
          <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        </client>
        <services>
          <service behaviorConfiguration="RoutingServiceBehavior" 
                   name="System.ServiceModel.Routing.RoutingService">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost/CustomerService/"/>
              </baseAddresses>
            </host>
            <endpoint address="" binding="basicHttpBinding" 
               bindingConfiguration="BasicHttpBinding_ICustomer" 
               contract="System.ServiceModel.Routing.IRequestReplyRouter" 
               name="RoutingServiceEndpoint">
            </endpoint>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="RoutingServiceBehavior">
              <!-- To avoid disclosing metadata information, set the value below 
                 to false and remove the metadata endpoint above before deployment -->
              <serviceMetadata httpGetEnabled="true"/>
              <!-- To receive exception details in faults for debugging purposes, 
                 set the value below to true. Set to false before deployment 
                 to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="true"/>
              <routing  filterTableName="routingRules" routeOnHeadersOnly="False"/>
            </behavior>
            <behavior name="">
              <serviceMetadata httpGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="false" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <routing>
          <namespaceTable>
            <add prefix="cc" 
              namespace="http://schemas.datacontract.org/2004/07/CustomerContract" />
          </namespaceTable>
          <filters>
            <filter name="PremiumCustomerFilter" filterType="XPath" 
                 filterData="//cc:CustomerCreditRating = 'Good'"/>
            <filter name="OrdinaryCustomerFilter" 
                 filterType="XPath" filterData="//cc:CustomerCreditRating = 'Bad'"/>
          </filters>
          <filterTables>
            <filterTable name="routingRules">
              <add filterName="PremiumCustomerFilter" 
                 endpointName="CustomerServiceLibrary_PremiumCustomerService" 
                 priority="0"/>
              <add filterName="OrdinaryCustomerFilter" 
                 endpointName="CustomerServiceLibrary_OrdinaryCustomerService" 
                 priority="0"/>
            </filterTable>
          </filterTables>
          <backupLists>
            <backupList name="CustomerBackupList">
              <add endpointName="CustomerServiceLibrary_OrdinaryCustomerService"/>
            </backupList>
          </backupLists>
        </routing>
      </system.serviceModel>
     <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
      </system.webServer>
    </configuration>

    Here we see that we have implemented routing using filter tables, filters, and XPath expressions. The filter tables implement routing rules which presents the corresponding endpoints for the filters which are evaluated using the XPath expression. The contents of the CustomerCreditRating public property of the Customer class is used to determine the endpoint for the downstream service to which the message would be forwarded subsequently.

  27. Now we can test the service in IIS Manager to see that the URL is http://localhost/CustomerService/CustomerService.svc.
  28. Next we add a Windows Forms application called ProtocolBridgingClient to the solution and add a service reference corresponding to the routing service: http://localhost/CustomerService/CustomerService.svc. We also add a reference to System.ServiceModel.dll.
  29. The form's code-behind file ProtocolBridgingForm.cs should have the following code:
  30. using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    
    namespace ProtocolBridgingClient
    {
        public partial class ProtocolBridgingForm : Form
        {
            public ProtocolBridgingForm()
            {
                InitializeComponent();
            }
    
            private void ProtocolBridgingForm_Load(object sender, EventArgs e)
            {
                try
                {
                    BasicHttpBinding binding = new BasicHttpBinding();
                    EndpointAddress address = new EndpointAddress(
                       "http://localhost/CustomerService/CustomerService.svc");
                    CustomerContract.ICustomer proxy = 
                       ChannelFactory<CustomerContract.ICustomer>.CreateChannel(
                       binding, address);
                    CustomerContract.Customer cust1 = 
                       new CustomerContract.Customer { CustomerID = "ar0045855", 
                       CustomerName = "Ambar Ray", CustomerCreditRating = "Good" };
                    CustomerContract.Customer cust2 = 
                       new CustomerContract.Customer { CustomerID = "am0046067", 
                       CustomerName = "Abhijit Mahato", CustomerCreditRating = "Bad" };
                    string res1 = proxy.GetCustomerDetails(cust1);
                    string res2 = proxy.GetCustomerDetails(cust2);
                    txtCustomer.Text = res1 + "\n" + res2;
                }
                catch (Exception ex)
                {
                    txtCustomer.Text = (ex.InnerException != null) ?  
                       ex.InnerException.Message + "\t" + ex.Message : ex.Message;
                }
            }
    
            private void ProtocolBridgingForm_FormClosed(object sender, 
                                                         FormClosedEventArgs e)
            {
                GC.Collect();
            }
    
        }
    }

    In the form load event, we first create a BasicHttpBinding object and then an EndpointAddress object specifying the URL of the routing service. Then, we create the proxy object by using the ChannelFactory.CreateChannel() method. We create the Customer objects, with one having 'Good' CustomerCreditRating and another having 'Bad' CustomerCreditRating, such that the former finally gets routed to the premium customer service and the latter gets routed to the ordinary customer service. We then call the GetCustomerDetails method and collect the output in the rich text box.

Points of Interest

This could be the basis for creating an ESB (Enterprise Service Bus) using WCF. We can have one router service for each schema / contract and corresponding downstream services operating on the respective schema / contract. This way, just submitting the message to the appropriate routing service helps in determining the message's 'itinerary' in the downstream systems depending on the contents of the message. The mapping of messages could be implemented using readily available open source code. Thus, a lightweight ESB could be created having routing along with transformation capabilities.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

AmbarRay
Architect Pramerica Systems Ireland
India India
CORE COMPETENCIES
 
 Design and Architecture for Microsoft SOA implementations using BizTalk, WCF, WF, SQL Server Integration Services & ESB Toolkit.
 
 Web Application design and implementation using ASP.NET MVC with jQuery & Entity Framework with LINQ for persistence layer.
 
 Designing and developing desktop applications using WPF, WinForms.
 
 SQL Server Database design & programming
 
EXPERIENCE SUMMARY
 
 Eleven years total, out of which 04 years as architect, 07 years as designer and developer in various Microsoft technologies like WinForms, ASP.NET WebForms, ASP.NET MVC, BizTalk, WCF, WF, WPF, SQL Server, LINQ, EF etc. Worked in various roles mainly architect, designer, team leader and developer.
 
 Hands on experience with ASP.NET web applications (both MVC and Web Forms) and desktop based applications as well as smart client applications using latest technologies harnessing .NET Framework 4.0, C# 4.0, ASP.NET 4.0, WCF, WF, WPF and LINQ.
 
 Hands-on working experience in Application integration, business process management and service oriented applications using BizTalk Server 2010, ESB Toolkit 2.1, WCF 4.0 services and SQL Server 2008 Integration Services.
 
 Thorough working knowledge of OOAD and agile / incremental development process.
 
 Experience in leading team of developers for on schedule project delivery with quality.
 
 Experience with low level programming like embedded C, C++ as well as systems programming on unix platform.
REMARKABLE PROFESSIONAL ACHIEVEMENTS
 
 Got Microsoft Community Contributor Award in year 2011 with MCC ID# 4034514.
 
 Published article in MSDN Magazine Feb 2011 edition on MDM with F#: http://msdn.microsoft.com/en-us/magazine/gg598923.aspx
http://www.codeproject.com/News/14767/Pattern-Matching-Database-Records-with-Fsharp.aspx
 
 Published highly popular article on BizTalk in www.dotnetcurry.com
http://www.dotnetcurry.com/ShowArticle.aspx?ID=683
 
 Umpteen blogs in BizTalk server forums and ESB toolkit forums.

Comments and Discussions

 
GeneralMy vote of 5 PinprofessionalMohammed Hameed8-Jul-13 20:46 
Questionhow do you make the NetTcpBinding_ICustomerService work ? Pinmemberlouise512-Mar-12 6:43 
Questionrequest Load balancing in WCF Router Pinmembercleelakumar7-Dec-11 20:06 
AnswerRe: request Load balancing in WCF Router Pinmemberakash_ju00731-Dec-11 20:57 
AnswerRe: request Load balancing in WCF Router PinmemberMember 101253823-Sep-13 21:27 
GeneralMy vote of 5 Pinmembersimon jarslev2-Nov-11 2:00 
QuestionRe: Content Based Routing Using WCF 4 PinmemberAfter205030-Aug-11 1:03 
GeneralMy vote of 4 PinmemberPatrick Kalkman28-Mar-11 20:16 
GeneralMissing backupList entry in filterTable Pinmemberarnab_pal21-Feb-11 18:41 
GeneralMy vote of 5 Pinmemberarnab_pal21-Feb-11 18:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 29 Jul 2010
Article Copyright 2010 by AmbarRay
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid