Click here to Skip to main content
Licence CPOL
First Posted 28 Jul 2010
Views 23,401
Downloads 467
Bookmarked 17 times

Content Based Routing Using WCF 4

By | 28 Jul 2010 | Article
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)

About the Author

AmbarRay

Software Developer (Senior)
Microsoft Community Contributor (MCC ID # 4034514)
India India

Member

#Experience Summmary:
• Total experience nine years with four years as an Architect.
• Worked in various roles mainly architect, designer, team leader and developer.
• Hands on experience with ASP.NET web applications and desktop based applications as well as smart client applications using latest technologies harnessing .NET Framework 3.5, C# 2008, ASP.NET 3.5, WCF, WF, WPF, LINQ and ADO.NET Entity Framework.
• Hands-on working experience in Application integration, business process management and service oriented applications using BizTalk Server 2009, ESB Toolkit 2.0, WCF services and SQL Server 2008 Integration Services.
• Thorough working knowledge of OOAD and incremental development process.
• Hands on experience with use case based unified data modeling.
• Experience with low level programming like embedded C, C++ as well as systems programming on unix platform.
 
• MTech(IT) from Punjabi University with 80% in year 2003 and MTech(CS) from IETE with 72% in year 2002.
• BE (CSE) from Shivaji University with 78% and 5th rank in University in year 1998.
• DEPM from CEDT with 76% in year 1994.
• SSC from WB Board with 83% in year 1991.
 
#Address:
DF-I B-2/4 PURBA ABASAN
1582/1 Rajdanga Main Road
Near Kasba New Market
Kolkata – 700107
Mobile : 9836071286
E-mail :private:ambarray@gmail.com
 
#Capabilities:
1. Enterprise Application Integration and Application Connectivity Solutions using BizTalk Server 2009 and ESB Toolkit 2.0.
2. SQL Server 2008 database, Integration Services, Reporting Services, Notification Services.
3. Web, Desktop and Handheld devices Application Development using ASP.NET 3.5, C# 3.5 with SQL Server 2008 R2, Oracle 11g.
4. Distributed application development using ASP.NET 3.5 web services and WCF.
5. OOAD, SOA, unix systems programming and embedded system development.
 
#Certifications:
*MCAD.NET
*70-235 TS:Developing Business Process and Integration Solutions using Microsoft BizTalk Server 2006

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Questionhow do you make the NetTcpBinding_ICustomerService work ? Pinmemberlouise56:43 12 Mar '12  
Questionrequest Load balancing in WCF Router Pinmembercleelakumar20:06 7 Dec '11  
AnswerRe: request Load balancing in WCF Router Pinmemberakash_ju00720:57 31 Dec '11  
GeneralMy vote of 5 Pinmembersimon jarslev2:00 2 Nov '11  
QuestionRe: Content Based Routing Using WCF 4 PinmemberAfter20501:03 30 Aug '11  
GeneralMy vote of 4 PinmemberPatrick Kalkman20:16 28 Mar '11  
GeneralMissing backupList entry in filterTable Pinmemberarnab_pal18:41 21 Feb '11  
GeneralMy vote of 5 Pinmemberarnab_pal18:33 21 Feb '11  
GeneralMy vote of 5 PinmemberMember 33480602:26 3 Nov '10  
GeneralMy vote of 5 Pinmemberavi_das2:12 2 Nov '10  
GeneralVery good article Pinmemberavi_das2:11 2 Nov '10  
GeneralMy vote of 5 PinmemberVinoth_KT20:08 25 Oct '10  
Questionnice article. how about security while routing? Pinmembercortadillo081522:52 19 Oct '10  
GeneralClient donst work?. All serivecs are working in IIS Pinmemberscott_dani7813:49 25 Aug '10  
GeneralRe: Client donst work?. All serivecs are working in IIS PinmemberAmbarRay18:34 25 Aug '10  
GeneralRe: Client donst work?. All serivecs are working in IIS Pinmemberscott_dani786:31 26 Aug '10  
GeneralRe: Client donst work?. All serivecs are working in IIS PinmemberAmbarRay0:10 27 Aug '10  
GeneralRe: Client donst work?. All serivecs are working in IIS Pinmemberscott_dani786:58 27 Aug '10  
GeneralRe: Client donst work?. All serivecs are working in IIS PinmemberAmbarRay14:55 27 Aug '10  
GeneralMy vote of 5 PinmemberMember 146997519:59 29 Jul '10  
GeneralMy vote of 4 PinmemberPartha Sengupta19:58 29 Jul '10  
GeneralRe: My vote of 4 PinmemberAmbarRay4:40 30 Jul '10  

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.

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120517.1 | Last Updated 29 Jul 2010
Article Copyright 2010 by AmbarRay
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid