Service Versioning (WCF Routing)

When we built up service versioning mechanism we need to consider the following items;

– service contract changes
– data contract changes
– service binding changes

I have tried to cover all possible scenarios which consist of various service changes. The ways of satisfying backward compatibility as follows:

1. Service address changes are acceptable

If the service address can be changed, the version management is so easy. All the versions of the service can be hosted different addresses which contains version information of the service.

https://application-address:application-port/Services/TestServiceV1.svc
https://application-address:application-port/Services/TestServiceV2.svc

2. Service address changes are NOT acceptable

In this scenario, we can use WCF Routing mechanism to serve many versions over the same address. We need to have a router service that is placed in the following address.

https://application-address:application-port/Services/TestService.svc

This router service sends requests to TestServiceV1 or TestServiceV2 depends on the filters defined in configuration part of router service. Each client application will insert the filter 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.

6

The filter types are as follows:

2.1.    Endpoint Name: A custom message filter for use solely in the Routing Service, which filters messages based on the name of the service endpoint.

2.2.    XPath: Used to match header or body elements of SOAP message to a particular value.

2.2.1.     The version info can be kept in the header

2.2.2.     The username can be placed in header therefore version can be done based on user over a username-version mapping.

2.3.    Action: Used to route based on the SOAP action of a message.

2.4.    And: Used to combine filters before a match can be satisfied.

2.5.    Custom: Used to supply a custom filtering component where you have programmatic control of the filter result.

3. Commercial Products in the Market

There are two major products which are generally called XML gateway or firewall. The first one is that leading product Layer7 XML Gateway (http://www.layer7tech.com/products/industry-leading-xml-gateway-overview). It is java based and running over the Linux server. It cannot be applicable for .NET environment. The second one .Net based product which is called Sentinet produced by Nevatech Inc. (https://www.nevatech.com/sentinet/soa-management). It is extension of Microsoft MSE (Managed Services Engine) framework.

Finally I have decided to keep version information in SOAP message header. WCF Router service will route the messages depends on their SOAP header version value. I have made a solution that can be seen following pic.

5

 

 

Let’s begin with implementation of TestServiceV1. The interface of TestServiceV1:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfPoc.Services.TestServiceV1
{
 [ServiceContract(Namespace="http://WcfPoc.wcfRouting.int/Increment1")]
 public interface ITestService
 {
 [OperationContract]
 List<Query> GetSavedQueries(string missionCode);
 }

[DataContract]
 public class Query
 {
 [DataMember(Order = 1)]
 public string Name { get; set; }

[DataMember(Order = 2)]
 public string Description { get; set; }

[DataMember(Order = 3)]
 public string TestType { get; set; }
 }
}

The second version of TestServiceV1 contains one more method which is called WriteTestEntities and it uses the TestEntity class that is newly added. Finally we have more than one changes which are service contract and data contract changes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfPoc.Services.TestServiceV2
{
 [ServiceContract(Namespace = "http://WcfPoc.wcfRouting.int/Increment1")]
 public interface ITestService
 {
 [OperationContract]
 List<Query> GetSavedQueries(string missionCode);

[OperationContract]
 bool WriteTestEntities (string missionCode, TestEntity entity);
 }

[DataContract]
 public class Query
 {
 [DataMember(Order = 1)]
 public string Name { get; set; }

[DataMember(Order = 2)]
 public string Description { get; set; }

[DataMember(Order = 3)]
 public string TestType { get; set; }

[DataMember(Order = 3)]
 public string TestTypeIdentifier { get; set; }
 }

[DataContract]
 public class TestEntity
 {
 [DataMember(Order = 1)]
 public string Capacity { get; set; }

[DataMember(Order = 2)]
 public string AirRefuelingType { get; set; }

[DataMember(Order = 3)]
 public string TestType { get; set; }

[DataMember(Order = 3)]
 public string TestTypeIdentifier { get; set; }
 }
}

After implementation of services, we need to host these services. In order to host TestServices I have used console applciation. TestServiceV1 and TestServiceV2 host applications as follows:

Host Application of TestServiceV1


using System;
using System.ServiceModel;
using WcfPoc.Services.TestServiceV1;
using WcfPoc.Host.Common;
using System.ServiceModel.Description;

namespace WcfPoc.Host.TestServiceV1
{
class Program
{
static void Main(string[] args)
{
var host = new ServiceHost(typeof(TestService));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(WcfPoc.Services.TestServiceV1.ITestService),
new BasicHttpBinding("TestServiceBinding"),
"http://localhost:8081/TestV1/TestService");
endpoint.Behaviors.Add(new ServiceMessagLogger());

try
{
host.Open();
Console.WriteLine("TestServiceV1 have started.");
Console.WriteLine("Address: http://localhost:8081/TestV1/TestService.svc");
Console.ReadLine();
host.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
host.Abort();
Console.ReadLine();
}
}
}
}

Host Application of TestServiceV2

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using WcfPoc.Host.Common;
using WcfPoc.Services.TestServiceV2;

namespace WcfPoc.Host.TestServiceV2
{
 class Program
 {
 static void Main(string[] args)
 {
 var host = new ServiceHost(typeof(TestService));
 ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(WcfPoc.Services.TestServiceV2.ITestService),
 new BasicHttpBinding("TestServiceBinding"),
 "http://localhost:8082/TestV2/TestService");
 endpoint.Behaviors.Add(new ServiceMessagLogger());

 try
 {
 host.Open();
 Console.WriteLine("TestServiceV2 have started.");
 Console.WriteLine("Address: http://localhost:8082/TestV2/TestService.svc");
 Console.ReadLine();
 host.Close();
 }
 catch (Exception ex)
 {
 Console.WriteLine(ex.Message);
 host.Abort();
 Console.ReadLine();
 }
 }
 }
}

The TestService versions are not known by the end user. The user just know Router service that I am gonna tell the details. The user knows interfaces of TestService V1 and V2. The users who are using both v1 and v2 send the requests to the same address not to http://localhost:8081/TestV1 or http://localhost:8082/TestV2.

Host Application of Router Service

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Routing;
using WcfPoc.Host.Common;

namespace WcfPoc.Host.TestRouterService
{
 class Program
 {
 static void Main(string[] args)
 {
 var host = new ServiceHost(typeof(RoutingService));
 ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(System.ServiceModel.Routing.IRequestReplyRouter),
 new BasicHttpBinding(),
 "http://localhost:8080/TestService/router");
 endpoint.Behaviors.Add(new ServiceMessagLogger());

 try
 {
 host.Open();
 Console.WriteLine("TestRoutingService have started.");
 Console.WriteLine("Address: http://localhost:8080/TestService.svc");
 Console.ReadLine();
 host.Close();
 }
 catch (Exception ex)
 {
 Console.WriteLine(ex.Message);
 host.Abort();
 Console.ReadLine();
 }
 }
 }
}

As you can see Router service not so much different from TestService, it is implementation of System.ServiceModel.Routing.IRequestReplyRouter interface and hosted in http://localhost:8080/TestService.svc. The tricky point and routing mechanism are in the RouterService configuration file.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <system.serviceModel>
 <services>
 <service name="System.ServiceModel.Routing.RoutingService" 
 behaviorConfiguration="routerConfig">
 <!--<endpoint address="http://localhost:8080/TestService/router"
 binding="basicHttpBinding"
 contract="System.ServiceModel.Routing.IRequestReplyRouter"
 name="reqReplyEndpoint" />-->
 </service>
 </services>
 <behaviors>
 <serviceBehaviors>
 <behavior name="routerConfig">
 <routing filterTableName="RoutingTable" routeOnHeadersOnly="false" />
 <serviceDebug includeExceptionDetailInFaults="true"/>
 </behavior>
 </serviceBehaviors>
 </behaviors>
 <client>
 <endpoint name="TestServiceV1"
 address="http://localhost:8081/TestV1/TestService"
 binding="basicHttpBinding"
 contract="*" />
 <endpoint name="TestServiceV2" 
 address="http://localhost:8082/TestV2/TestService" 
 binding="basicHttpBinding" 
 contract="*" />
 </client>
 <routing>
 <namespaceTable>
 <add prefix="s" namespace="http://schemas.xmlsoap.org/soap/envelope/"/>
 <add prefix="cn" namespace="http://WcfPoc.wcfRouting.int/Increment1"/>
 </namespaceTable>
 <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'"/>
 </filters>
 <filterTables>
 <filterTable name="RoutingTable">
 <add filterName="firstVersionFilter" endpointName="TestServiceV1"/>
 <add filterName="secondVersionFilter" endpointName="TestServiceV2"/>
 </filterTable>
 </filterTables>
 </routing>
 </system.serviceModel>
</configuration>

In the filters part there are two filters are defined as firversionFilter and secondVersionFilter. If the request has Version info is equal to v1.0 than the router service will route the request to the TestServiceV1. Also I handle the case that the request doesnt have version info inth request header. In that case the router service will throw an exception to the user. The router service usage scenarios are as follows;

1. TestClientV1 and TestClientV2 are sending request to router service.
3
2. The request has no header info.

4

Have nice coding…

Advertisements

2 thoughts on “Service Versioning (WCF Routing)

  1. Hi ibrhmoguz , very nice entry and good idea. Can you please share the code for the common dll as it seems it is missing, or even better, can you share the code
    Thanks in advance

    Like

    • Ibrhmoguz, any feedback, I really want to follow those concepts,c an you please provide the dll
      Thanks inadvance

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s