How to make use of workflow correlation token to continue the process flow from an active instance?
Continue from the previous post about SQL persistence, assume now your workflow application is enabled with SQL persistence. The following are the steps to create a sample project that work with workflow instance store.
Create a new database for the sample project. I am going to create a simple leave application, there are only 2 operations in the application which are apply leave then approve leave. So, I have created a table for it. Note that I have a column call "WorkflowCorrelationID", it will be used later to show you how workflow persistence work.
I will skip the details about the common reptitive work such as creating business entities, business layer and data access layer, I use LASG to generate them all. Again, if you do not know what is LASG, refer to my previous post. This tool generates all those code for you. I have generated the sample application code in just few minutes. Thanks to Serena. This is how my solution look like after using LASG to generate all the code from data access layer all the way up to business layer then custom workflow activities and the workflow activity designer. The custom workflow activities are actually Code Activity, and they will appear in the control toolbox after project is built and to be used in the next step to construct the workflow.
Design the workflow and here is how it look like.
Configure the first Receive operation to able to create new instance when it get hit. Provide and standardize the Service Contract Name.
Configure the workflow correlation handler first. In my case is __handle1 which was already created automatically in the variable section.
Select the object property that you wish Workflow handler to use as the key to correlate the workflow instance with application instance. Note: the value must be unique, hence I use GUID. And, note that in my step 6, when I create my table, I have a column call "WorkflowCorrelationID" and here is the purpose of it. Workflow rely this unique token to identify the workflow instances in the persistence store.
Now you would want the instance data get persisted after the Apply Leave operation is completed. So, set the PersistBeforeSend to true in the SendReplyToReceive.
After apply leave, we need to approve leave. Imagine that we have many different people apply leave but they are all pending for approval. There will be many workflow instances are created for each individual leave application. Here is how correlation work and Workflow base on the correlation ID and know which workflow instance to pick and resume the expected operation which is approve leave. So, in the approve leave's Receive operation, we should also set the correlation handler and token same as the apply leave's Receive operation. But this time, I am not going to create new instance when hit this approve leave operation because we should not do that since we already had an instance created when we apply leave, we should reuse back the same instance earlier.
How to expose Workflow as WCF service?
Workflow part is completed. Now, I want to expose my workflow as a WCF service. Here is how my Windows Process Activation Service configuration look like in my web.config.
Note the highlight for the key difference of the configuration between Workflow hosted WCF Service and normal WCF Service.
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
<serviceActivations>
<add factory="System.ServiceModel.Activities.Activation.WorkflowServiceHostFactory"
relativeAddress="./LeaveWorkflowService.svc" service="PersistenceStoreSample.Workflows.LeaveWorkflowService"/>
<add factory="System.ServiceModel.Activation.ServiceHostFactory"
relativeAddress="./LeaveService.svc" service="PersistenceStoreSample.Services.LeaveService"/>
</serviceActivations>
</serviceHostingEnvironment>
<services>
<service name="LeaveWorkflowService"
behaviorConfiguration="WorkflowServiceBehavior">
<endpoint name="basicHttpLeaveWorkflowService"
address=""
binding="basicHttpBinding"
contract="ILeaveWorkflowService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
<service name="PersistenceStoreSample.Services.LeaveService"
behaviorConfiguration="DefaultServiceBehavior">
<endpoint name="basicHttpLeaveService"
address=""
binding="basicHttpBinding"
contract="PersistenceStoreSample.Services.Contracts.ILeaveService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WorkflowServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<sqlWorkflowInstanceStore connectionStringName="WorkflowInstanceStore"
hostLockRenewalPeriod="00:00:30"
runnableInstancesDetectionPeriod="00:00:05"
instanceCompletionAction="DeleteAll"
instanceLockedExceptionAction="AggressiveRetry"
instanceEncodingOption="GZip"
/>
</behavior>
<behavior name="DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
Note: The service host factory for Workflow is different from WCF, thanks to Serena for pointing it out.
For WCF, System.ServiceModel.Activation.ServiceHostFactory
For Workflow, System.ServiceModel.Activities.Activation.WorkflowServiceHostFactory
How to call the Workflow service?
I have created a unit project to test my workflow.
First step, run apply leave unit test. A new workflow instance will be created, and a new leave application record will be created in my database. You can track your workflow instance and status by looking at the [System.Activities.DurableInstancing].[InstancesTable] table in the WorkflowInstanceStore database.
Note that the value in the IsInitialized column, it mean this instance has been initialized and persisted. Also, note that BlockingBookmarks column, it tells this instance is expecting ApproveLeave operation. If you perform any other operation for this instance, you would get an error. If you happen to run into error with your workflow application, then your instance will get suspended (you can know your instance is suspended by referring to the value in the IsSuspended column), then you can either terminate it or restore it.
Second step, run approve leave unit test. The previously created workflow instance will be continued. Workflow know which instance to pick by using the correlation token that you passed in earlier. Now if you look at the InstancesTable, you will find the instance record is disappear. The reason is the workflow process is completed, and the instance record is deleted. If you want the instance record remain, you can set the instance completion action in the the web.config.
<behavior name="WorkflowServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<sqlWorkflowInstanceStore
connectionStringName="WorkflowPersistenceStore"
hostLockRenewalPeriod="00:00:30"
runnableInstancesDetectionPeriod="00:00:05"
instanceCompletionAction="DeleteNothing"
instanceLockedExceptionAction="AggressiveRetry"
instanceEncodingOption="GZip"/>
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
That is all about Workflow SQL Persistence. Next topic I will share how to track workflow activity status, instance status, and logging the error in event viewer or SQL server.
If you wish to have my complete sample project source code, feel free to download it from HERE.
Continue from the previous post about SQL persistence, assume now your workflow application is enabled with SQL persistence. The following are the steps to create a sample project that work with workflow instance store.
Create a new database for the sample project. I am going to create a simple leave application, there are only 2 operations in the application which are apply leave then approve leave. So, I have created a table for it. Note that I have a column call "WorkflowCorrelationID", it will be used later to show you how workflow persistence work.
I will skip the details about the common reptitive work such as creating business entities, business layer and data access layer, I use LASG to generate them all. Again, if you do not know what is LASG, refer to my previous post. This tool generates all those code for you. I have generated the sample application code in just few minutes. Thanks to Serena. This is how my solution look like after using LASG to generate all the code from data access layer all the way up to business layer then custom workflow activities and the workflow activity designer. The custom workflow activities are actually Code Activity, and they will appear in the control toolbox after project is built and to be used in the next step to construct the workflow.
Design the workflow and here is how it look like.
Configure the first Receive operation to able to create new instance when it get hit. Provide and standardize the Service Contract Name.
Configure the workflow correlation handler first. In my case is __handle1 which was already created automatically in the variable section.
Select the object property that you wish Workflow handler to use as the key to correlate the workflow instance with application instance. Note: the value must be unique, hence I use GUID. And, note that in my step 6, when I create my table, I have a column call "WorkflowCorrelationID" and here is the purpose of it. Workflow rely this unique token to identify the workflow instances in the persistence store.
Now you would want the instance data get persisted after the Apply Leave operation is completed. So, set the PersistBeforeSend to true in the SendReplyToReceive.
After apply leave, we need to approve leave. Imagine that we have many different people apply leave but they are all pending for approval. There will be many workflow instances are created for each individual leave application. Here is how correlation work and Workflow base on the correlation ID and know which workflow instance to pick and resume the expected operation which is approve leave. So, in the approve leave's Receive operation, we should also set the correlation handler and token same as the apply leave's Receive operation. But this time, I am not going to create new instance when hit this approve leave operation because we should not do that since we already had an instance created when we apply leave, we should reuse back the same instance earlier.
How to expose Workflow as WCF service?
Workflow part is completed. Now, I want to expose my workflow as a WCF service. Here is how my Windows Process Activation Service configuration look like in my web.config.
Note the highlight for the key difference of the configuration between Workflow hosted WCF Service and normal WCF Service.
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
<serviceActivations>
<add factory="System.ServiceModel.Activities.Activation.WorkflowServiceHostFactory"
relativeAddress="./LeaveWorkflowService.svc" service="PersistenceStoreSample.Workflows.LeaveWorkflowService"/>
<add factory="System.ServiceModel.Activation.ServiceHostFactory"
relativeAddress="./LeaveService.svc" service="PersistenceStoreSample.Services.LeaveService"/>
</serviceActivations>
</serviceHostingEnvironment>
<services>
<service name="LeaveWorkflowService"
behaviorConfiguration="WorkflowServiceBehavior">
<endpoint name="basicHttpLeaveWorkflowService"
address=""
binding="basicHttpBinding"
contract="ILeaveWorkflowService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
<service name="PersistenceStoreSample.Services.LeaveService"
behaviorConfiguration="DefaultServiceBehavior">
<endpoint name="basicHttpLeaveService"
address=""
binding="basicHttpBinding"
contract="PersistenceStoreSample.Services.Contracts.ILeaveService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WorkflowServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<sqlWorkflowInstanceStore connectionStringName="WorkflowInstanceStore"
hostLockRenewalPeriod="00:00:30"
runnableInstancesDetectionPeriod="00:00:05"
instanceCompletionAction="DeleteAll"
instanceLockedExceptionAction="AggressiveRetry"
instanceEncodingOption="GZip"
/>
</behavior>
<behavior name="DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
Note: The service host factory for Workflow is different from WCF, thanks to Serena for pointing it out.
For WCF, System.ServiceModel.Activation.ServiceHostFactory
For Workflow, System.ServiceModel.Activities.Activation.WorkflowServiceHostFactory
How to call the Workflow service?
I have created a unit project to test my workflow.
[TestMethod] public void ApplyLeaveTest() { //Create new leave application Leave leave = new Leave(); leave.ApplyBy = 1; leave.ApplicationDate = DateTime.Now; //Generate correlation token first before invoke workflow service leave.WorkflowCorrelationID = Guid.NewGuid(); LeaveWorkflowServiceClient wfLeave = new LeaveWorkflowServiceClient(); wfLeave.ApplyLeave(leave); } [TestMethod] public void ApproveLeaveTest() { LeaveServiceClient wcfLeave = new LeaveServiceClient(); //Get existing leave application from DB Leave leave = wcfLeave.GetLeave(1); //Set the approval detail as like approving the leave leave.ApproveBy = 100; leave.ApprovalDate = DateTime.Now; LeaveWorkflowServiceClient wfLeave = new LeaveWorkflowServiceClient(); wfLeave.ApproveLeave(leave); }
First step, run apply leave unit test. A new workflow instance will be created, and a new leave application record will be created in my database. You can track your workflow instance and status by looking at the [System.Activities.DurableInstancing].[InstancesTable] table in the WorkflowInstanceStore database.
Note that the value in the IsInitialized column, it mean this instance has been initialized and persisted. Also, note that BlockingBookmarks column, it tells this instance is expecting ApproveLeave operation. If you perform any other operation for this instance, you would get an error. If you happen to run into error with your workflow application, then your instance will get suspended (you can know your instance is suspended by referring to the value in the IsSuspended column), then you can either terminate it or restore it.
Second step, run approve leave unit test. The previously created workflow instance will be continued. Workflow know which instance to pick by using the correlation token that you passed in earlier. Now if you look at the InstancesTable, you will find the instance record is disappear. The reason is the workflow process is completed, and the instance record is deleted. If you want the instance record remain, you can set the instance completion action in the the web.config.
<behavior name="WorkflowServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<sqlWorkflowInstanceStore
connectionStringName="WorkflowPersistenceStore"
hostLockRenewalPeriod="00:00:30"
runnableInstancesDetectionPeriod="00:00:05"
instanceCompletionAction="DeleteNothing"
instanceLockedExceptionAction="AggressiveRetry"
instanceEncodingOption="GZip"/>
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
That is all about Workflow SQL Persistence. Next topic I will share how to track workflow activity status, instance status, and logging the error in event viewer or SQL server.
If you wish to have my complete sample project source code, feel free to download it from HERE.
Im getting the following error after deploying the WF in IIS.
ReplyDeleteSystem.ServiceModel.FaultException
HResult=0x80131501
Message=The execution of an InstancePersistenceCommand was interrupted because the instance key '01ff8031-a0da-df05-9989-701db6522fa6' was not associated to an instance. This can occur because the instance or key has been cleaned up, or because the key is invalid. The key may be invalid if the message it was generated from was sent at the wrong time or contained incorrect correlation data.
Source=mscorlib
StackTrace:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at DI.Chakra.Workflow.ServiceHelpers.ProvisioningApprovalWFServiceReference.IWFService.ApprovalRequest(Int64 taskId, String workflowInstanceId)
at DI.Chakra.Workflow.ServiceHelpers.ProvisioningApprovalWFServiceReference.WFServiceClient.ApprovalRequest(Int64 taskId, String workflowInstanceId) in C:\Projects\V5_MSTeam\SP\V5\SOURCE\Workflows\DI.Chakra.Workflow.ServiceHelpers\Service References\ProvisioningApprovalWFServiceReference\Reference.cs:line 109
at DI.Chakra.Workflow.ServiceHelpers.Provisioning.ProvisioningApprovalWFServiceHelper.ApprovalRequest(Int64 taskId, String workflowInstanceId) in C:\Projects\V5_MSTeam\SP\V5\SOURCE\Workflows\DI.Chakra.Workflow.ServiceHelpers\Service Helpers\Provisioning\ProvisioningApprovalWFServiceHelper.cs:line 25
at DI.Chakra.Workflow.ServiceHelpers.ServiceAccessStrategy.AccessProvisioningService() in C:\Projects\V5_MSTeam\SP\V5\SOURCE\Workflows\DI.Chakra.Workflow.ServiceHelpers\ServiceAccessStrategy.cs:line 84
at DI.Chakra.Workflow.ServiceHelpers.ServiceAccessStrategy.AccessWorkflowService() in C:\Projects\V5_MSTeam\SP\V5\SOURCE\Workflows\DI.Chakra.Workflow.ServiceHelpers\ServiceAccessStrategy.cs:line 28
at DI.Chakra.DomainApi.Controllers.Workflow.WorkflowController.SubmitWorkflowCriteria(PresentationWorkflowCriteriaBase workflowCriteria) in C:\Projects\V5_MSTeam\SP\V5\SOURCE\DI.Chakra.DomainApi\Controllers\Workflow\WorkflowController.cs:line 23
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.b__9(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
Plz suggest Thanks