New Features of WCF 4.0: Part III

In this series of articles, I want to talk about the new features in the area of Windows Communication Foundation (WCF) in order to improve the development experience, enable more communication scenario, support new WS-* standards and provide a good integration with Windows Workflow Foundation (WF).

Previous Articles
 
 

Introduction

 
Microsoft.NET 4.0 and Visual Studio.NET 2010 ships a lot of new features in their underlying technologies. In this series of articles, I want to talk about the new features in the area of Windows Communication Foundation (WCF) in order to improve the development experience, enable more communication scenario, support new WS-* standards and provide a good integration with Windows Workflow Foundation (WF).
 
The new features are essentially the following: simplified configuration, standard endpoints, IIS hosting without a SVC file, support for WS-Discovery, routing service, REST improvements, enhancements for the integration with WF to support workflow services, simple byte stream encoding and ETW tracing.
 
In this series of article, I will illustrate each feature explaining the principles and showing some examples.
 

Routing service

 
Routing service is one of the most interesting features in WCF 4.0. This approach is aimed at taking advantage of centralized routing services that acts as brokers to the catalog of services in the organization. This makes to decouple the consumers from the services and to make some processing while the messages pass through the channel between the consumer and the service for example implement security services or use content-based routing techniques to determine the target service based on the content of a particular message.
 
In order to implement this routing service, WCF 4.0 provides a new class called RoutingService. The function of RoutingService is to receive incoming messages from consumers and to route them to the target service by evaluating each message against a set of message filters. You can host the RoutingService instances in your application like a traditional WCF service and can be configured either by code or by configuration. The configuration is done by enabling the RoutingBehavior on the RouterService, and then by specifying the name of the filter table.
 
In order to illustrate the principles of routing service, I'm going to develop a RoutingService as a content-based router. Our example scenario is an order shipping service implemented using the principles of routing services in WCF 4.0. The orders, sent by the clients, are processed by the routing service and submitted to the target shipping service according to a tag in the message to identify two possible outcomes: by plane or by bus. One important advantage is that the clients don't know the target services.
 
The first step is to define the shipping service contract and order shipping data contract for the messages in the ShippingServiceContract project. The ShippingServiceContract is shown in the Listing 1.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ServiceModel;  
  6. namespace ShippingServiceContract  
  7. {  
  8.     [ServiceContract]  
  9.     public interface IShippingService  
  10.     {  
  11.         [OperationContract]  
  12.         void Submit(ShippingOrder shippingOrder);  
  13.     }  
  14. }
Listing 1
 
And the definition of the ShippingOrder message is shown in the Listing 2. 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Runtime.Serialization;  
  6. namespace ShippingServiceContract  
  7. {  
  8.     [DataContract]  
  9.     public class ShippingOrder  
  10.     {  
  11.         [DataMember]  
  12.         public int ShippingId { getset; }  
  13.         [DataMember]  
  14.         public string Origin { getset; }  
  15.         [DataMember]  
  16.         public string Destination { getset; }  
  17.         [DataMember]  
  18.         public string Order { getset; }  
  19.         /// <summary>  
  20.         /// If ShippingMethod is 1 then by Plane.  
  21.         /// If ShippingMethod is 2 then by Bus.  
  22.         /// </summary>  
  23.         [DataMember]  
  24.         public int ShippingMethod { getset; }  
  25.     }  
  26. }
Listing 2
 
Next step is to implement the service contract in two different services: PlaneShippingService and BusShippingService. The PlaneShippingService is implemented in the PlaneShippingServiceCons as shown in the following listings: Listing 3, Listing 4 and Listing 5.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using ShippingServiceContract;  
  6.    
  7. namespace PlaneShippingServiceCons  
  8. {  
  9.     public class PlaneShippingService : IShippingService  
  10.     {  
  11.         #region IShippingService Members  
  12.         public void Submit(ShippingOrder shippingOrder)  
  13.         {  
  14.             System.Console.WriteLine("ShippingOrder Info. ShippingId {0}. ShippingMethod {1}", shippingOrder.ShippingId, shippingOrder.ShippingMethod);  
  15.         }  
  16.         #endregion  
  17.     }  
  18. }
Listing 3
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ServiceModel;  
  6.    
  7. namespace PlaneShippingServiceCons  
  8. {  
  9.     class Program  
  10.     {  
  11.         static void Main(string[] args)  
  12.         {  
  13.             ServiceHost serviceHost = new ServiceHost(typeof(PlaneShippingService), new Uri("http://localhost:8080/Services/PlaneShippingService"));  
  14.             serviceHost.Open();  
  15.             System.Console.WriteLine("Press any key to finish the service ...");  
  16.             System.Console.ReadLine();  
  17.         }  
  18.     }  
  19. }
Listing 4
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.           <system.serviceModel>  
  4.                    <behaviors>  
  5.                              <serviceBehaviors>  
  6.                                       <behavior name="">  
  7.                                                 <serviceMetadata httpGetEnabled="true" />  
  8.                                       </behavior>  
  9.                              </serviceBehaviors>  
  10.                    </behaviors>  
  11.           </system.serviceModel>  
  12. </configuration>
Listing 5
 
The BusShippingService is implemented in the BusShippingServiceCons as shown in the following listings: Listing 6, Listing 7 and Listing 8.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using ShippingServiceContract;  
  6.    
  7. namespace BusShippingServiceCons  
  8. {  
  9.     public class BusShippingService : IShippingService  
  10.     {  
  11.         #region IShippingService Members  
  12.         public void Submit(ShippingOrder shippingOrder)  
  13.         {  
  14.             System.Console.WriteLine("ShippingOrder Info. ShippingId {0}. ShippingMethod {1}", shippingOrder.ShippingId, shippingOrder.ShippingMethod);  
  15.         }  
  16.         #endregion  
  17.     }  
  18. }
Listing 6
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ServiceModel;  
  6. namespace BusShippingServiceCons  
  7. {  
  8.     class Program  
  9.     {  
  10.         static void Main(string[] args)  
  11.         {  
  12.             ServiceHost serviceHost = new ServiceHost(typeof(BusShippingService), new Uri("http://localhost:8080/Services/BusShippingService"));  
  13.             serviceHost.Open();  
  14.             System.Console.WriteLine("Press any key to finish the service ...");  
  15.             System.Console.ReadLine();  
  16.         }  
  17.     }  
  18. }
Listing 7
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.           <system.serviceModel>  
  4.                    <behaviors>  
  5.                              <serviceBehaviors>  
  6.                                       <behavior name="">  
  7.                                                 <serviceMetadata httpGetEnabled="true" />  
  8.                                       </behavior>  
  9.                              </serviceBehaviors>  
  10.                    </behaviors>  
  11.           </system.serviceModel>  
  12. </configuration>
Listing 8
 
Next step is to define the routing service. We need to add another console project to the solution and a reference to the System.ServiceModel.dll and System.ServiceModel.Routing.dll assemblies (see Figure 1).
 
1.gif  
Figure 1
 
Then, we're going to host a RoutingService instance as a traditional WCF service as shown in the Listing 9. This service will act as a broker. 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ServiceModel;  
  6. using System.ServiceModel.Routing;  
  7.   
  8. namespace ShippingRoutingServiceCons  
  9. {  
  10.     class Program  
  11.     {  
  12.         static void Main(string[] args)  
  13.         {  
  14.             ServiceHost serviceHost = new ServiceHost(typeof(RoutingService));  
  15.             serviceHost.Open();  
  16.             System.Console.WriteLine("Press any key to finish the service ...");  
  17.             System.Console.ReadLine();  
  18.         }  
  19.     }  
  20. }
Listing 9
 
Next step in the solution is to create the configuration for the routing service.
 
If you examine the documentation for the RoutingService class, you will find that this class implements four interfaces: IDuplexSessionRouter, IRequestReplyRouter, ISimplexDatagramRouter and ISimplexSessionRouter.
 
In our example, the main endpoint will expose the IRequestReplyRouter contract for the address http://localhost:8080/Services/ShippingRoutingService using the binding basicHttpBinding. Then, as part of the service behavior, I will assign a routing table with the routing rules for our scenario (see Listing 10). 
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.           <system.serviceModel>  
  4.                    <services>  
  5.                              <service name="System.ServiceModel.Routing.RoutingService" behaviorConfiguration="ShippingRoutingServiceBeh">  
  6.                                       <endpoint address="http://localhost:8080/Services/ShippingRoutingService" binding="basicHttpBinding"  
  7.                                                             contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
  8.                              </service>  
  9.                    </services>  
  10.                    <client>  
  11.                              <endpoint address="http://localhost:8080/Services/PlaneShippingService" binding="basicHttpBinding" contract="*" name="PlaneShippingService"/>  
  12.                              <endpoint address="http://localhost:8080/Services/BusShippingService" binding="basicHttpBinding" contract="*" name="BusShippingService"/>  
  13.                    </client>  
  14.                    <behaviors>  
  15.                              <serviceBehaviors>  
  16.                                       <behavior name="ShippingRoutingServiceBeh">  
  17.                                                 <routing filterTableName="ShippingRoutingServiceFilterTable" routeOnHeadersOnly="false"/>  
  18.                                       </behavior>  
  19.                              </serviceBehaviors>  
  20.                    </behaviors>  
  21.                    <routing>  
  22.                              <filters>  
  23.                                       <filter name="PlaneShippingServiceFilter" filterType="XPath" filterData="boolean(//*[local-name()='ShippingMethod']/text()=1)"/>  
  24.                                       <filter name="BusShippingServiceFilter" filterType="XPath" filterData="boolean(//*[local-name()='ShippingMethod']/text()=2)"/>  
  25.                              </filters>  
  26.                              <filterTables>  
  27.                                       <filterTable name="ShippingRoutingServiceFilterTable">  
  28.                                                 <add filterName="PlaneShippingServiceFilter" endpointName="PlaneShippingService"/>  
  29.                                                 <add filterName="BusShippingServiceFilter" endpointName="BusShippingService"/>  
  30.                                       </filterTable>  
  31.                              </filterTables>  
  32.                    </routing>  
  33.           </system.serviceModel>  
  34. </configuration>
Listing 10
 
Finally, we're going to implement the client-side component of our solution. We're going to communicate to the shipping routing service using dynamic mechanism implemented in the ChannelFactory class without the need to create a proxy representing the services (the bus and plance shipping services). The only requirement is to reference the ShippingServiceContract.dll assembly (see Figure 2) in order to use the service definition.
 
2.gif  
Figure 2
 
We're going to host our client components in a console application too.
 
The code for the implementation of the client-side is shown in the Listing 11.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.ServiceModel;  
  6. using ShippingServiceContract;  
  7. namespace ConsoleApplication2  
  8. {  
  9.     class Program  
  10.     {  
  11.         static void Main(string[] args)  
  12.         {  
  13.             BasicHttpBinding basicBinding = new BasicHttpBinding();  
  14.             EndpointAddress endpointAddress = new EndpointAddress("http://localhost:8080/Services/ShippingRoutingService");  
  15.             IShippingService shippingSvcProxy = ChannelFactory<IShippingService>.CreateChannel(basicBinding, endpointAddress);  
  16.             ShippingOrder shippingOrder = new ShippingOrder();  
  17.             shippingOrder.ShippingId = 1;  
  18.             shippingOrder.ShippingMethod = 1;  
  19.             shippingSvcProxy.Submit(shippingOrder);  
  20.         }  
  21.     }  
  22. }
Listing 11
 

Conclusion

 
In this series of article, I've explained the new features of WCF 4.0 through concepts and examples.
 
Next Articles