I usually compile my workflows into a DLL and host them in IIS/WAS with Windows Server AppFabric. Personally, I felt that this is the best and easiest way to deploy enterprise-grade, scalable workflow solutions. In this post, I'm going to share with you how workflow services versioning can be achieved using the WorkflowServiceHostFactory (WSHF) approach.
If you don't already know, hosting workflow services using the WSHF is super easy. Everything can be configured in the host web.config file. One of the tips I would like to share is the file-less activation feature introduced in WF 4.0 which do not require us to create .svc files for our workflows. Here's a snippet of how it looks like.
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
<serviceActivations>
<add factory="System.ServiceModel.Activities.Activation.WorkflowServiceHostFactory"
relativeAddress="./ExpenseWorkflowService.svc"
service="WFVersioning.Workflows.ExpenseWorkflowService" />
</serviceActivations>
</serviceHostingEnvironment>
The reason I'm showing you this is because of the System.ServiceModel.Activities.Activation.WorkflowServiceHostFactory. We will be replacing that with our own later. :) In case you are unsure of how to configure a workflow service, it looks something like the following:
<service name="ExpenseWorkflowService"
behaviorConfiguration="WorkflowServiceBehavior">
<endpoint name="basicHttpWorkflowService"
address=""
binding="basicHttpBinding"
contract="IExpenseWorkflowService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
To enable persistence for our workflows, we will require to configure the persistence store. Remember to have it configured in the serviceBehavior.
<serviceBehaviors>
<behavior name="WorkflowServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<sqlWorkflowInstanceStore connectionStringName="workflowStore"
hostLockRenewalPeriod="00:00:30"
runnableInstancesDetectionPeriod="00:00:05"
instanceCompletionAction="DeleteAll"
instanceLockedExceptionAction="AggressiveRetry"
instanceEncodingOption="GZip" />
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</serviceBehaviors>
The above configuration refers to a connection string name workflowStore which must be configured in the connectionStrings settings (Not shown here).
Here's how the Version 1 of the ExpenseWorkflowService looks like for this example:
With the previous configuration, the workflow service should run fine when hosted on IIS/WAS. However, assuming now the requirements have changed and we are required to deploy a newer version of the service which supports cancellation. The Version 2 of our workflow service will look like the following:
This would break any running workflow instances that have not been completed i.e. Expenses that were submitted but pending approval. To support the new workflow, we will first need to create the new version in a new workflow xaml file and give it a different name i.e. ExpenseWorkflowServiceV2. You must give it another name. You cannot rename the old one to reuse the name. It will cause your running instances to fail if you do.
Next, create a custom WorkflowServiceHostFactory in the host project.
public class ExpenseWorkflowServiceHostFactory : WorkflowServiceHostFactory
{
protected override WorkflowServiceHost CreateWorkflowServiceHost(
Activity activity, Uri[] baseAddresses)
{
// Current workflow service.
WorkflowService current = new WorkflowService
{
Name = "ExpenseWorkflowService",
Body = new ExpenseWorkflowServiceV2(),
DefinitionIdentity = new WorkflowIdentity
{
Name = "Version_2",
Version = new Version(2, 0, 0, 0)
}
};
// Older version.
WorkflowService version1 = new WorkflowService
{
Name = "ExpenseWorkflowService",
Body = new ExpenseWorkflowService(),
};
// Create WorkflowServiceHost
WorkflowServiceHost host =
new WorkflowServiceHost(current, baseAddresses);
host.SupportedVersions.Add(version1);
return host;
}
}
- Remember to override the CreateWorkflowServiceHost method that accepts an Activity parameter. The one that accepts WorkflowService is for Xamlx-based services.
- Version 1 does not have a DefinitionIdentity because the initial instances created by the default WSHF does not contain any.
- Remember to add the older versions to the SupportedVersions collection of the host.
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
<serviceActivations>
<add factory="WFVersioning.Hosts.Web.ExpenseWorkflowServiceHostFactory"
relativeAddress="./ExpenseWorkflowService.svc"
service="WFVersioning.Workflows.ExpenseWorkflowService" />
</serviceActivations>
</serviceHostingEnvironment>
Once this is done, the workflow service should be able to support both the new and old versions. The behavior will be:
- Newer client consuming the service will invoke the new workflow service version.
- Older clients will invoke the older workflow service version (inclusive of new instances).
- Any running instances will continue to use the older workflow service version.
There you go! Hope you find it useful :)
If you want to build Workflow Services quickly, do check-out my tool - Layered Architecture Solution Guidance :)
No comments:
Post a Comment