Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#5.0

WCF Routing Service - Part IV: Service Versioning & Multicasting

4.83/5 (11 votes)
6 Jun 2014CPOL16 min read 22.8K   545  
This article describes implementation of Service Versioning & using the RoutingService.

Table of Contents

All Posts

Introduction

In this article, I'll explore two more features of the RoutingService, Service Versioning & Multicasting. I'll discuss a very simple Service Versioning scenario in this post and will see how it can be solved by using the RoutingService. Then I'll implement Muticasting using the RoutingService. You can also implement Multicasting using the udpBinding came with the WCF 4.5. So let's dive in Multicasting & Service Versioning.

Service Versioning

After initial development, a service is deployed on the production server in order to make it available for the end-users. But in future, there might be some changes needed in the service in order to full-fill the business or technology requirements or even to address some issues. Each change results a new version of the service and you might have to maintain the old versions in parallel with the new one to serve the existing end-users of the service.

So how would you tackle the existing end-users of old versions of the service as you can’t force them to use the new version immediately? Well you should have a concrete plan for the same. You can consider limiting the number of concurrent old versions of the service through governance in future. You can think about gradual migration of the existing end-users of old version of the service to new version by retiring the old versions gracefully. There might be lots of strategies depending upon the categories of the change in the most recent version of the service and you'll need to adopt accordingly. These are out of the scope for this post. You can go through the details by browsing to the links provided in the Reference section at the bottom of this article.

So this was a brief description of the Service Versioning. In this post we’ll restrict our self to implement Service Versioning using the RoutingService only. With the RoutingService one can implement Service Versioning using Content-based or Context-based routing techniques easily. Let’s start with considering a scenario.

Service Versioning Scenario

I’ll start with our familiar ComplexNumberCalculator service from previous posts of this series. Suppose that there are two versions of the same; v1.0 and v2.0. v1.0 of the service exposes following Binary Operations-

  • Add
  • Subtract
  • Multiply
  • Divide

I’ll refer this version as ‘Regular’. V2.0 of the service exposes both Binary & Unary Operations respectively as below-

  • Add
  • Subtract
  • Multiply
  • Divide
  • Modulus
  • Argument
  • Conjugate
  • Reciprocal

I’ll refer this version of the service as ‘Extended’. Below are the class diagrams of the both versions of the service-

Image 1

So there are two versions of the ComplexNumberCalculator service (Regular & Extended) and our goal is to support both versions simultaneously in order to serve end-users of the each version. We’ll solve this Service Versioning problem using the RoutingService.

But before winding up this section, I would like to share some brief details about the both versions of the ComplexNumberCalculator service. I’ve hosted each version of the ComplexNumberCalculator service in two separate console applications and you can see the implementation details of the both in attached sample code provided with this post.

I’ve configured ComplexNumberCalculator service (v1.0) with following single endpoint along with a standard mex endpoint-

ASP.NET
<services>
      <service name="RegularComplexNoCalc.ComplexNoCalc">
        <endpoint address="Regular" binding="basicHttpBinding" contract="RegularComplexNoCalc.IComplexNumber" />
        <endpoint address="mex" kind="mexEndpoint" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8081/ComplexNumberCalculator" />
          </baseAddresses>
        </host>
      </service>
</services> 

I’ve configured ComplexNumberCalculator service (v2.0) with following single endpoint along with a standard mex endpoint-

ASP.NET
<services>
      <service name="ExtendedComplexNoCalc.ComplexNoCalc">
        <endpoint address="Extended" binding="basicHttpBinding" contract="ExtendedComplexNoCalc.IComplexNumber" />
        <endpoint address="mex" kind="mexEndpoint" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8082/ComplexNumberCalculator" />
          </baseAddresses>
        </host>
      </service>
 </services> 

Configuring the RoutingService

The problem described in the previous section can be resolved using the RoutingService by following two techniques-

  • Context-based routing technique
  • Content-based routing technique

In Context-based routing technique, a specific endpoint is exposed for each version of the service in the router and incoming messages are uniquely routed to a specific version of the service based on the specific endpoint on which messages arrive.

So in our case we’ll need to expose two endpoints in the router one for each service version. But this technique isn’t feasible because we’ll need to expose more endpoints in the router for each newer version of the service.

In Content-based routing technique, a single specific endpoint is exposed for all version of the service in the router and incoming messages are uniquely routed to a specific version of the service based on the content of the message.

So in our case we’ll need to expose a single endpoint in the router for two versions of the service. This technique would be preferable because it is based on inspecting content of the incoming messages in order to differentiate between requests for the different service versions.

But how would we differentiate between requests for the two service versions in our case by inspecting content of the incoming messages? Because Binary Operations are common in both versions of the service, so requests for the two service versions would be identical in all aspects (their input parameters and return types are same) in this case. The content of the incoming messages would not be unique enough to determine the correct target service version. Although in case of Unary Operations requests, we could easily determine the correct target service version. But in overall, we can’t use Action or XPath filter types in order to determine the correct target service version as requests for Binary Operations would be identical. You can verify the same by going through the Action values of each service version as shown below-.

ASP.NET
<!-- Action values of first version of the service-->

http://tempuri.org/IComplexNumber/Add
http://tempuri.org/IComplexNumber/Subtract
http://tempuri.org/IComplexNumber/Multiply
http://tempuri.org/IComplexNumber/Divide

<!-- Action values of second version of the service-->

http://tempuri.org/IComplexNumber/Add
http://tempuri.org/IComplexNumber/Subtract
http://tempuri.org/IComplexNumber/Multiply
http://tempuri.org/IComplexNumber/Divide
http://tempuri.org/IComplexNumber/Modulus
http://tempuri.org/IComplexNumber/Argument
http://tempuri.org/IComplexNumber/Conjugate
http://tempuri.org/IComplexNumber/Reciprocal 

So what would be the solution? Well this problem can be solved by inserting the service version information into a request message header. Each client application will insert the target service version information into the message header and router will this version-specific information contained in the message header to route the incoming message to the appropriate version of the service.

Image 2

Now in our case, we’ll insert a custom element ‘Version’ into the request message header to indicate the target service version that the request message would be transmitted to. The custom element Version can have two possible values- v1.0 & v2.0. A value of v1.0 will indicate that the request message must be routed to the first version of the ComplexNumberCalculator service to be processed while a value of v2.0 will indicate that the request message would be routed to the second version of the ComplexNumberCalculator service to be processed.

So in our case, SOAP message will look like as shown down below-

ASP.NET
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Version xmlns="http://custom/namespace">v1.0</Version>
<To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:8081/ComplexNumberCalculator/Regular</To>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IComplexNumber/Add</Action>
</s:Header>
<s:Body>
...
</s:Body>
</s:Envelope> 

In the above, 'http://custom/namespace' is a namespace I've defined for the custom element inserted into the request message header.

I’ll explain how we can insert a custom element into the message header in the next section. But before that, let’s configure the RoutingService for our Service Versioning scenario.

So First I've configured the RoutingService with the following virtual endpoint-

ASP.NET
<services>
      <service name="System.ServiceModel.Routing.RoutingService">
        <endpoint address="" binding="basicHttpBinding" contract="System.ServiceModel.Routing.IRequestReplyRouter" name="VirtualEndpoint" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8080/RoutingService/Router" />
          </baseAddresses>
        </host>
      </service>
</services> 

Next I've defined two target endpoints one for each service version-

ASP.NET
<client>
      <endpoint address="http://localhost:8081/ComplexNumberCalculator/Regular" binding="basicHttpBinding"
                contract="*" name="firstVersion" />
      <endpoint address="http://localhost:8082/ComplexNumberCalculator/Extended" binding="basicHttpBinding"
                contract="*" name="secondVersion" />
</client> 

Next I've enabled the RoutingBehavior followed by specifying the name of the filter table. I've done this by defining default behavior as below-

ASP.NET
<behaviors>
      <serviceBehaviors>
        <behavior name="">
          <routing filterTableName="RoutingTable" />
        </behavior>
      </serviceBehaviors>
</behaviors> 

Next I've defined a set of namespace prefix bindings using the the <namespacetable> element as below-

ASP.NET
<namespaceTable>
        <add prefix="s" namespace="http://schemas.xmlsoap.org/soap/envelope/"/>
        <add prefix="cn" namespace="http://custom/namespace"/>
</namespaceTable> 

Next I've defined filters for each version of the ComplexNumberCalculator service with the help of the custom element 'Version' using the XPath filterType as shown down below-

ASP.NET
<filters>
        <filter name="firstVersionFilter" filterType="XPath" filterData="/s:Envelope/s:Header/cn:Version ='v1.0'"/>
        <filter name="secondVersionFilter" filterType="XPath" filterData="/s:Envelope/s:Header/cn:Version ='v2.0'"/>
        <filter name="noHeaderFilter" filterType="XPath" filterData="count(/s:Envelope/s:Header/cn:Version) = 0" />
</filters> 

Note that I’ve defined above an extra filter to handle the incoming messages without having ‘Version’ element in their header.

Finally I've mapped each filter to the respective target service endpoint in the filter table 'RoutigTable' as shown down below-

ASP.NET
<filterTables>
        <filterTable name="RoutingTable">
          <add filterName="firstVersionFilter" endpointName="firstVersion"/>
          <add filterName="secondVersionFilter" endpointName="secondVersion"/>
          <add filterName="noHeaderFilter" endpointName="secondVersion"/>
        </filterTable>
</filterTables> 

Notice that above I’ve mapped noHeaderFilter filter to the most recent version of the service. But other strategies could be considered as per the requirement.

That's it. Let's move to the next section.

Inserting Custom Elements in the outgoing Message Headers

In order to simulate our Service Versioning demo, I've created two console applications to mimic the clients (end-users) one for each version of the ComplexNumberCalculator service. I've configured first client application with following endpoint-

ASP.NET
<system.serviceModel>
    <client>
      <endpoint address="http://localhost:8080/RoutingService/Router"
        binding="basicHttpBinding" contract="RegularComplexNoCalc.IComplexNumber" name="firstVersionEndUsers" />
    </client>
</system.serviceModel> 

Then I've configured second client application with following endpoint-

ASP.NET
<system.serviceModel>
    <client>
      <endpoint address="http://localhost:8080/RoutingService/Router"
        binding="basicHttpBinding" contract="ExtendedComplexNoCalc.IComplexNumber" name="secondVersionEndUsers" />
    </client>
</system.serviceModel> 

That's it. Nothing special.

Let's examine the client applications code and see how can we insert a custom element into the SOAP header of the outgoing message to indicate the target service version information. Below is the code of the first client application (end-users of the first version of the service)-

C#
var cfV1 = new ChannelFactory<IComplexNumber>("firstVersionEndUsers");
var channelV1 = cfV1.CreateChannel();            

var z1 = new ComplexNumber();
var z2 = new ComplexNumber();

z1.Real = 3D;
z1.Imaginary = 4D;

z2.Real = 4D;
z2.Imaginary = 3D;

Console.WriteLine("*** Service Versioning: : end-users of the first version ***\n");
Console.WriteLine("\nPlease hit any key to run OR enter 'exit' to terminate.");
string command = Console.ReadLine();

while (command != "exit")
{
      Console.WriteLine("Please hit any key to simulate firstVersionEndUsers: ");
      Console.ReadLine();

      using (new OperationContextScope((IContextChannel)channelV1))
      {
         OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Version", "http://custom/namespace", "v1.0"));
         ComplexNumberArithmetics(channelV1, z1, z2);   
      }

      Console.WriteLine("\nPlease hit any key to re-run OR enter 'exit' to terminate.");
      command = Console.ReadLine();
}
            
((IClientChannel)channelV1).Close(); 

In the above code, first I've created a client side proxy at runtime using the ChannelFactory class in oder to invoke the members of the first version of the service. Now before doing the same, we'll need to insert a custom element for holding versioning information into the SOAP header of the outgoing message.

So next I've created an instance of the OperationContextScope class to get the current operation context scope, based on the client side proxy’s innerChannel. Note that here (IContextChannel)channelV1 represents the client side proxy’s innerChannel. It is very important to note that, If you have created the client side proxy using the svcutill generated proxy class (in this case ComplexNumberClient class, ComplexNumberClient proxy = new ComplexNumberClient("firstVersionEndUsers")) at compile time, then you'll need to invoke proxy.InnerChannel in order to get the client side proxy’s innerChannel. One more important point; the instance of the OperationContextScope class need to be created first, before accessing the outgoing message headers element from the context (OperationContext.Current.OutgoingMessageHeaders). If you do the same, the OperationContext will null and you'll face an exception- System.NullreferenceException: {"Object reference not set to an instance of an object."}

Next I've created a new custom element by invoking the MessageHeader.CreateHeader method and added it into the outgoing message heders by invoking the OperationContext.Current.OutgoingMessageHeaders.Add method. Here 'Version', 'http://custom/namespace' and 'v1.0' represents name, namespace and value of the custom element respectively. Notice that here I've hard-coded the version of the target service. You can save it in the configuartion file of the client application or in the database or even in the simple text file as per your requirement.

Finally I've invoked ComplexNumberArithmetics method to access the members of the first version of the service. You can find the code of the ComplexNumberArithmetics method in the sample code attached with this post.

The code of the second client application is similar except for the value of the 'Version' custom element (v2.0). Please see the sample attached with this post.

Simulating the Service Versioning using the RoutingService

Now all is done. Let's run the demo application. Below is the screen shot of the solution-

Image 3

Just set ClientOfV1, ClientOfV2, HostExtendedCalc, HostRegularCalc & Router projects as Start Up projects and hit Ctrl+F5 keys in order to run the projects. Now minimize the RoutingService console window.

Next press any key on the first client application window (end-users of the first version of the service); you can verify that the incoming messages are routed to the first version of the ComplexNumberCalculator service (Regular) by the intermediary RoutingService after inspecting the version information contained in the incoming messages header.

Image 4

Next press any key on the second client application window (end-users of the second version of the service); you can verify that the incoming messages are routed to the second version of the ComplexNumberCalculator service (Extended) by the intermediary RoutingService after inspecting the version information contained in the incoming messages header.

Image 5

Now just close all running applications (clients, services and router) and comment out following line of code from each client application (end-users of each service version) and re-build the solution in order to simulating the case of incoming messages without having ‘Version Information’ in their headers.

C#
...
using (new OperationContextScope((IContextChannel)channelV1))
{
   //OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("Version", "http://custom/namespace", "v1.0"));
   ComplexNumberArithmetics(channelV1, z1, z2);  
}   
...            

Notice that I’ve already defined a filter in ‘Configuring the RoutingService’ section to handle this type situation. Finally set ClientOfV1, ClientOfV2, HostExtendedCalc, HostRegularCalc & Router projects as Start Up projects and hit Ctrl+F5 keys in order to run the projects and minimize the RoutingService console window.

Next just press any key on the first client application window (end-users of the first version of the service) followed by pressing any key on the second client application (end-users of the second version of the service); you can verify that the incoming messages are routed to the second version of the ComplexNumberCalculator service (Extended) in both cases by the intermediary RoutingService. It is as per our expectation as we’ve already fingered this situation in the filer table.

Image 6

So you have seen that how different versions of a service could be made available simultaneously using the RoutingService. Here we’ve achieved Service Versioning by inserting the service version information into the outgoing message headers but there are other techniques of Service Versioning that don’t require client applications to pass additional information through outgoing messages headers. A message could be routed to the most recent or most compatible version of a service.

Multicasting

What is Multicasting? Multicast is the term used to describe communication where a piece of information is sent from one or more points to a set of other points. In Multicasting, sender >= 1 (i.e. there should be at least one sender) and receiver >= 0 (i.e. there may be no receivers).

In Multicasting, clients receive a stream of packets (data) only if they have previously subscribed to a specific multicast group and membership of a group is dynamic and controlled by the receivers. Multicasting is very useful if a group of end-users require a common set of data at the same time e.g. in stock exchanges, multimedia content delivery networks, IPTV applications (distance learning and televised company meetings), wireless networks and cable-TV etc. With the help of Multicasting, you can reach to many end-users by utilizing only one data stream and hence decreasing the amount of bandwidth and saving your money.

After brief description about Multicasting, let's see how can we implement Multicasting using the WCF RoutingService.

Multicasting Demo Service

I'll use a simple Stock Service to demonstrate the Multicasting using the RoutingService in this post. The Stock Service will send brief information of stocks (name, its type and its instantaneous price) to each subscribed receivers simultaneously to all subscribed receivers after each pre-defined time span.

Image 7

Below is the class diagram of the service-

Image 8

I've created a StockService using One-Way mwssages. Below are the definitions of DataContract & ServiceContract respectively along with the implementation details of the StockService-

C#
[DataContract]
public class Stock
{
   [DataMember]
   public string Name;

   [DataMember]
   public string StockType;

   [DataMember]
   public double Price;
}

[ServiceContract]
public interface IStockService
{
   [OperationContract(IsOneWay = true)]
   void SendStockDetail(Stock stock);
}

public class StockService : IStockService
{
   public void SendStockDetail(Stock stock)
   {
      Console.WriteLine(string.Format("Stock Name: {0}, Stock Type: {1}, Price: ${2:000.00}", stock.Name, stock.StockType, stock.Price));
   }
}

Finally I’ve hosted StockService in a console application and configured a single endpoint along with a standard mex endpoint as shown below-

ASP.NET
<services>
   <service name="StockInformation.StockService">
      <endpoint address="" binding="basicHttpBinding" contract="StockInformation.IStockService" />
      <endpoint address="mex" kind="mexEndpoint" />
      <host>
         <baseAddresses>
            <add baseAddress="http://localhost:8081/StockService" />
         </baseAddresses>
      </host>
   </service>
</services>

Please note that each instance of this service will act as a receiver in our Multicasting demo. Let’s move to the next section to configure the RoutingService for the Multicasting.

Configuring the RoutingService for Multicasting

I'll re-configure our RoutingService used in 'Service Versioning' demo earlier in this post for the Multicasting demo. So first I've re-configured the RoutingService with the following virtual endpoint-

ASP.NET
<services>
    <service name="System.ServiceModel.Routing.RoutingService">
      <endpoint address="" binding="basicHttpBinding"
                contract="System.ServiceModel.Routing.ISimplexDatagramRouter" name="virtualendpoint" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8080/RoutingService/Router" />
        </baseAddresses>
      </host>
    </service>
</services>

Notice that I've used here ISimplexDatagramRouter ServiceContract as StockService is designed to process only One-Way messages. Next I've re-defined following three target endpoints one for each service instance. These instances will act as receivers in our Multicasting demo. Actually here I've just subscribed receivers in order to receive multicast information from the sender(s).

ASP.NET
<client>
    <endpoint address="http://localhost:8081/StockService1" binding="basicHttpBinding"
              contract="*" name="stockservice1" />
    <endpoint address="http://localhost:8081/StockService2" binding="basicHttpBinding"
              contract="*" name="stockservice2" />
    <endpoint address="http://localhost:8081/StockService3" binding="basicHttpBinding"
              contract="*" name="stockservice3" />
</client>

Next I've re-defined a single filter using the MatchAll filter type that will matches all incoming messages.

ASP.NET
<filters>
      <filter name="wildCardFilter" filterType="MatchAll" />
</filters>

As our goal is to just multicast all incoming messages from the sender(s) to the subscribed receiver(s), so next I've re-configured the filter table 'RoutingTable' by mapping the above defined wildCardFilter filter to all target endpoints (receivers) as shown down below.

ASP.NET
<filterTables>
      <filterTable name="RoutingTable">
        <add filterName="wildCardFilter" endpointName="stockservice1" />
        <add filterName="wildCardFilter" endpointName="stockservice2" />
        <add filterName="wildCardFilter" endpointName="stockservice3" />
      </filterTable>
</filterTables>

This finishes the RoutingService configuration for Multicasting demo. Let's move to the next section in order to mimic the sender(s) for the Multicasting.

Mimicking the Sender for Multicasting

The next step would be to mimic the sender(s) application for our Multicasting demo in order to send stock information after each pre-defined time span to the subscribed receivers. For this I've created a console client application of the StockService and configured it with following endpoint-

ASP.NET
<client>
      <endpoint address="http://localhost:8080/RoutingService/Router" binding="basicHttpBinding"
                contract="Sender.IStockService" name="stockDetails" />
</client>

Finally let's go through the client application code.

C#
var cf = new ChannelFactory<IStockService>("stockDetails");
var channel = cf.CreateChannel();

Console.WriteLine("*** Multicasting using RoutingService Demo ***\n");
Console.WriteLine("Please hit any key to start: ");
string command = Console.ReadLine();

while (command != "exit")
{
   var stock = GetStockDetails();
   channel.SendStockDetail(stock);

   Console.WriteLine("Details of the Stock: {0} has been sent; Sender: {1}", stock.Name);
   System.Threading.Thread.Sleep(3000);
}

((IClientChannel)channel).Close();

In the above code, first I've created a client side proxy at runtime using the ChannelFactory class in order to invoke the only member 'SendStockDetail' of the StockService. Next I've generated the stock information using the GetStockDetails method and multicasted it by invoking the SendStockDetail method. Note that I've simulated the stock information generation after each 3 seconds time span using the GetStockDetails method. You can find the code of the same in the sample example provided with this post.

Simulating the Multicasting using the RoutingService

Before running our demo, just go through the screen shot of the Multicasting solution provided with this post-

Image 9

Now just set Client & Router projects as Start Up projects and hit Ctrl+F5 keys in order to run the projects. Next minimize the RoutingService console window and run the WCFRoutingPart4\Multicasting\StockServices\StartAllServices.cmd file from the Visual Studio Developer Command Prompt (in Administrator mode) in order to start StockService1, StockService2 & StockService3 services.

Next press any key on the console client window (sender); you can verify that all incoming messages are being multicasted to all subscribed receivers by the intermediary RoutingService.

Image 10

Next just stop any one receiver; you can verify that now incoming messages are being multicasted to the available (active) receivers by the intermediary RoutingService.

Image 11

Next stop all active receivers; you can verify that incoming messages are still being multicasted by the intermediary RoutingService although there is no receiver. This is as per rule of the multicasting where there might be no receivers.

Image 12

Finally do one more experiment. Just start multiple instances of the client application (senders) as well as multiple instances of the StockService (receivers); you can verify that in this case incoming messages are being multicasted from the multiple senders to all subscribed receivers by the intermediary RoutingService. This is again as per rule of the multicasting where there might be more than one sender.

Image 13

Conclusion

Actually I've considered Service Contract Versioning as a Service Versioning scenario in this post. But there might be other scenarios of the Service Versioning like: Data Contract Versioning, Message Contract Versioning, Address & Binding Versioning etc. You'll need to outline and handle each cases of the Service Versioning scenarios before implementing the same.

As far as Multicasting is concerned, apart from just Multicasting the incoming messages to the subscribed receivers, you can also portioned receivers into the groups and can multicast incoming messages to a specific group based on some defined criteria. Let's say there are two groups of subscribed receivers of the StockService: G1 & G2, then we can multicast incoming messages with Price > $500 to the group G1 and incoming messages with Price <= $500 to the group G2 using the RoutingService.

So you have seen how Service Versioning & Multicasting features can easily be implemented using the RoutingService. Hope you've enjoyed this post.

References

History

  • 7th Jun, 2014 -- Original version posted

License

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