In my previous WF4 post I described the principal of how to version workflow services using the WCF 4 RoutingService. In that post I described the general problem and solution without going into a lot of detail and showing any code. In this blog post I will add an actual implementation you can use for reference purposes.
The basic layout
The solution has three parts.
- The workflow service with 2 different versions of same workflow. The second version of the workflow has more activities in the tree so it can’t load workflow instances from version 1. Each workflow is hosted in a different XAMLX file and the second was created by copying the first and making the required changes to it.
- The client application that is calling the service. It knows about one existing workflow instance previously created and will create a new one. Next it will send several requests to both workflows without knowing that these are implemented using different version.
- The routing service which receives all requests from the client and forwards them to the different workflow services depending on the data in the request.
The Workflow Services
The workflow version 1
The workflow version 2
The client application
The basic setup is that the first request, creating the new workflow instance, returns the version identifier and this is a mandatory item in each subsequent request. The client code that does this looks like this:
using System;
using TheClient.ServiceReference1;
namespace TheClient
{
class Program
{
static void Main(string[] args)
{
var proxy = new ServiceClient();
var version = proxy.StartOrder(2);
for (int i = 0; i < 5; i++)
{
Console.WriteLine(proxy.AddItem(1, "1.0"));
Console.WriteLine(proxy.AddItem(2, version));
}
proxy.Stop(2, version);
Console.ReadLine();
}
}
}
Along with this config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:8080/Service"
binding="basicHttpBinding"
contract="ServiceReference1.IService"
name="BasicHttpBinding_IService" />
</client>
</system.serviceModel>
</configuration>
The address “http://localhost:8080/Service” used here is the address of the routing service.
The WCF RoutingService
This is where the magic really happens. It checks the versionId parameter from each request and sends the request to the corresponding service. And if no version is found, i.e. a new workflow is started, the request is routed to the last version.
The code is pretty simple and looks like this:
using System;
using System.ServiceModel;
using System.ServiceModel.Routing;
namespace TheRouter
{
class Program
{
static void Main(string[] args)
{
var host = new ServiceHost(
typeof(RoutingService),
new Uri("http://localhost:8080/Service"));
host.Open();
Console.WriteLine("The RoutingService is listening");
Console.ReadLine();
host.Close();
}
}
}
Most of the work is done in the configuration file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:26606/Service_V1.xamlx"
binding="basicHttpBinding"
contract="*"
name="serviceV1"/>
<endpoint address="http://localhost:26606/Service_V2.xamlx"
binding="basicHttpBinding"
contract="*"
name="serviceV2"/>
</client>
<services>
<service name="System.ServiceModel.Routing.RoutingService">
<endpoint address=""
binding="basicHttpBinding"
contract="System.ServiceModel.Routing.IRequestReplyRouter"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<routing filterTableName="serviceRouting"
routeOnHeadersOnly="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<routing>
<filters>
<filter filterType="XPath"
name="serviceV1"
filterData="//ns:versionId='1.0'"/>
<filter filterType="XPath"
name="serviceV2"
filterData="//ns:versionId='2.0'"/>
<filter filterType ="MatchAll"
name="all"/>
</filters>
<namespaceTable>
<add prefix="ns"
namespace="http://tempuri.org/"/>
</namespaceTable>
<filterTables>
<filterTable name="serviceRouting">
<add endpointName="serviceV1"
filterName="serviceV1"
priority="2"/>
<add endpointName="serviceV2"
filterName="serviceV2"
priority="2"/>
<add endpointName="serviceV2"
filterName="all"
priority="1"/>
</filterTable>
</filterTables>
</routing>
</system.serviceModel>
</configuration>
Running this shows the following output showing that the different requests where send to the correct workflow
You can download the complete VS2010 solution with all code here.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu