Using WorkflowIdentity and Versioning

WorkflowIdentity provides a way for workflow application developers to associate a name and a Version with a workflow definition, and for this information to be associated with a persisted workflow instance. This identity information can be used by workflow application developers to enable scenarios such as side-by-side execution of multiple versions of a workflow definition, and provides the cornerstone for other functionality such as dynamic update. This topic provides as overview of using WorkflowIdentity with WorkflowApplication hosting. For information on side-by-side execution of workflow definitions in a workflow service, see Side by Side Versioning in WorkflowServiceHost. For information on dynamic update, see Dynamic Update.

In this topic

Using WorkflowIdentity

To use WorkflowIdentity, create an instance, configure it, and associate it with a WorkflowApplication instance. A WorkflowIdentity instance contains three identifying pieces of information. Name and Version contain a name and a Version and are required, and Package is optional and can be used to specify an additional string containing information such as assembly name or other desired information. A WorkflowIdentity is unique if any of its three properties are different from another WorkflowIdentity.

Important

A WorkflowIdentity should not contain any personally identifiable information (PII). Information about the WorkflowIdentity used to create an instance is emitted to any configured tracking services at several different points of the activity life-cycle by the runtime. WF Tracking does not have any mechanism to hide PII (sensitive user data). Therefore, a WorkflowIdentity instance should not contain any PII data as it will be emitted by the runtime in tracking records and may be visible to anyone with access to view the tracking records.

In the following example, a WorkflowIdentity is created and associated with an instance of a workflow created using a MortgageWorkflow workflow definition.

WorkflowIdentity identityV1 = new WorkflowIdentity
{
    Name = "MortgageWorkflow v1",
    Version = new Version(1, 0, 0, 0)
};

WorkflowApplication wfApp = new WorkflowApplication(new MortgageWorkflow(), identity);

// Configure the WorkflowApplication with persistence and desired workflow event handlers.
ConfigureWorkflowApplication(wfApp);

// Run the workflow.
wfApp.Run();

When reloading and resuming a workflow, a WorkflowIdentity that is configured to match the WorkflowIdentity of the persisted workflow instance must be used.

WorkflowApplication wfApp = new WorkflowApplication(new MortgageWorkflow(), identityV1);

// Configure the WorkflowApplication with persistence and desired workflow event handlers.
ConfigureWorkflowApplication(wfApp);

// Load the workflow.
wfApp.Load(instanceId);

// Resume the workflow...

If the WorkflowIdentity used when reloading the workflow instance does not match the persisted WorkflowIdentity, a VersionMismatchException is thrown. In the following example a load attempt is made on the MortgageWorkflow instance that was persisted in the previous example. This load attempt is made using a WorkflowIdentity configured for a newer version of the mortgage workflow that does not match the persisted instance.

WorkflowApplication wfApp = new WorkflowApplication(new MortgageWorkflow_v2(), identityV2);

// Configure the WorkflowApplication with persistence and desired workflow event handlers.
ConfigureWorkflowApplication(wfApp);

// Attempt to load the workflow instance.
wfApp.Load(instanceId);

// Resume the workflow...

When the previous code is executed, the following VersionMismatchException is thrown.

The WorkflowIdentity ('MortgageWorkflow v1; Version=1.0.0.0') of the loaded instance does not match the WorkflowIdentity ('MortgageWorkflow v2; Version=2.0.0.0') of the provided workflow definition. The instance can be loaded using a different definition, or updated using Dynamic Update.

Side-by-side Execution using WorkflowIdentity

WorkflowIdentity can be used to facilitate the execution of multiple versions of a workflow side-by-side. One common scenario is changing business requirements on a long-running workflow. Many instances of a workflow could be running when an updated version is deployed. The host application can be configured to use the updated workflow definition when starting new instances, and it is the responsibility of the host application to provide the correct workflow definition when resuming instances. WorkflowIdentity can be used to identify and supply the matching workflow definition when resuming workflow instances.

To retrieve the WorkflowIdentity of a persisted workflow instance, the GetInstance method is used. The GetInstance method takes the Id of the persisted workflow instance and the SqlWorkflowInstanceStore that contains the persisted instance and returns a WorkflowApplicationInstance. A WorkflowApplicationInstance contains information about a persisted workflow instance, including its associated WorkflowIdentity. This associated WorkflowIdentity can be used by the host to supply the correct workflow definition when loading and resuming the workflow instance.

Note

A null WorkflowIdentity is valid, and can be used by the host to map instances that were persisted with no associated WorkflowIdentity to the proper workflow definition. This scenario can occur when a workflow application was not initially written with workflow versioning, or when an application is upgraded from .NET Framework 4. For more information, see Upgrading .NET Framework 4 Persistence Databases to Support Workflow Versioning.

In the following example a Dictionary<WorkflowIdentity, Activity> is used to associate WorkflowIdentity instances with their matching workflow definitions, and a workflow is started using the MortgageWorkflow workflow definition, which is associated with the identityV1 WorkflowIdentity.

WorkflowIdentity identityV1 = new WorkflowIdentity
{
    Name = "MortgageWorkflow v1",
    Version = new Version(1, 0, 0, 0)
};

WorkflowIdentity identityV2 = new WorkflowIdentity
{
    Name = "MortgageWorkflow v2",
    Version = new Version(2, 0, 0, 0)
};

Dictionary<WorkflowIdentity, Activity> WorkflowVersionMap = new Dictionary<WorkflowIdentity, Activity>();
WorkflowVersionMap.Add(identityV1, new MortgageWorkflow());
WorkflowVersionMap.Add(identityV2, new MortgageWorkflow_v2());

WorkflowApplication wfApp = new WorkflowApplication(new MortgageWorkflow(), identityV1);

// Configure the WorkflowApplication with persistence and desired workflow event handlers.
ConfigureWorkflowApplication(wfApp);

// Run the workflow.
wfApp.Run();

In the following example, information about the persisted workflow instance from the previous example is retrieved by calling GetInstance, and the persisted WorkflowIdentity information is used to retrieve the matching workflow definition. This information is used to configure the WorkflowApplication, and then the workflow is loaded. Note that because the Load overload that takes the WorkflowApplicationInstance is used, the SqlWorkflowInstanceStore that was configured on the WorkflowApplicationInstance is used by the WorkflowApplication and therefore its InstanceStore property does not need to be configured.

Note

If the InstanceStore property is set, it must be set with the same SqlWorkflowInstanceStore instance used by the WorkflowApplicationInstance or else an ArgumentException will be thrown with the following message: The instance is configured with a different InstanceStore than this WorkflowApplication..

// Get the WorkflowApplicationInstance of the desired workflow from the specified
// SqlWorkflowInstanceStore.
WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(instanceId, store);

// Use the persisted WorkflowIdentity to retrieve the correct workflow
// definition from the dictionary.
Activity definition = WorkflowVersionMap[instance.DefinitionIdentity];

WorkflowApplication wfApp = new WorkflowApplication(definition, instance.DefinitionIdentity);

// Configure the WorkflowApplication with persistence and desired workflow event handlers.
ConfigureWorkflowApplication(wfApp);

// Load the persisted workflow instance.
wfApp.Load(instance);

// Resume the workflow...

Upgrading .NET Framework 4 Persistence Databases to Support Workflow Versioning

A SqlWorkflowInstanceStoreSchemaUpgrade.sql database script is provided to upgrade persistence databases created using the .NET Framework 4 database scripts. This script updates the databases to support the new versioning capabilities introduced in .NET Framework 4.5. Any persisted workflow instances in the databases are given default versioning values, and can then participate in side-by-side execution and dynamic update.

If a .NET Framework 4.5 workflow application attempts any persistence operations that use the new versioning features on a persistence database which has not been upgraded using the provided script, an InstancePersistenceCommandException is thrown with a message similar to the following message.

The SqlWorkflowInstanceStore has a database version of '4.0.0.0'. InstancePersistenceCommand 'System.Activities.DurableInstancing.CreateWorkflowOwnerWithIdentityCommand' cannot be run against this database version.  Please upgrade the database to '4.5.0.0'.

To upgrade the database schema

  1. Open SQL Server Management Studio and connect to the persistence database server, for example .\SQLEXPRESS.

  2. Choose Open, File from the File menu. Browse to the following folder: C:\Windows\Microsoft.NET\Framework\v4.0.30319\sql\en

  3. Select SqlWorkflowInstanceStoreSchemaUpgrade.sql and click Open.

  4. Select the name of the persistence database in the Available Databases drop-down.

  5. Choose Execute from the Query menu.

When the query completes, the database schema is upgraded, and if desired, you can view the default workflow identity that was assigned to the persisted workflow instances. Expand your persistence database in the Databases node of the Object Explorer, and then expand the Views node. Right-click System.Activities.DurableInstancing.Instances and choose Select Top 1000 Rows. Scroll to end of the columns and note that there are six additional columns added to the view: IdentityName, IdentityPackage, Build, Major, Minor, and Revision. Any persisted workflows will have a value of NULL for these fields, representing a null workflow identity.