Versioning a Visual Studio SharePoint Workflow

I have had the opportunity to work on many a SharePoint development projects.  One of the issues that comes up when developing custom workflows with SharePoint workflows is, “How do I update my custom workflow once the original version has been deployed?”.  SharePoint designer handles workflow versioning extremely well however until SharePoint 2010 these workflows are not reusable across sites or lists which can be a major drawback as if I wanted to use the same workflow in another site I would have to rebuild the workflow from scratch.  not to mention SharePoint designer workflows although powerful and handles a great number of scenarios are limited in functionality.  Enter Visual Studio custom workflows and the upgrade scenario mentioned above.  Visual Studio workflows leave the versioning problem to the developer and there is no real trivial way to handle this.  Daniel Odievich and Ali Mazaheri  has done a great job at documenting some best practices when you find yourself in this scenario.  I have copied the applicable section of their article here however the link to their larger article “Developing Workflow Solutions with SharePoint Server 2007 and Windows Workflow Foundation”  is below.

Saving Workflow State and Upgrading Workflow Assemblies

SharePoint Server implements its own workflow persistence service in the Workflow table in the SharePoint content database. SharePoint Server creates a persisted workflow instance by serializing the instance, compressing it, and storing it as a binary large object (BLOB) in an image column.

You may find it interesting to write a simple utility to extract the resulting binary BLOB, uncompress it by using the System.IO.Compression.GZipStream class (or by saving the stream as a .gz file and extracting contents by using a utility such as WinZip), and open it in Notepad to see the binary content generated by workflow persistence engine. If you do this, notice that, among all the binary data, the persisted state contains the following important references:

  • The full strong name of the workflow assembly, including its version number and public key token
  • An entry for each workflow activity (such as StateActivity, EventDrivenActivity, and IfElseActivity)
  • An entry for all private variables that you defined and their contents at the time of serialization

At some point, you will undoubtedly need to fix bugs in your workflow or add a new feature. Any change except trivial fixes likely requires adding a new StateActivity, StateTransition, IfElseBranch, or CodeActivity activity (or anything else that inherits from Activity). Or perhaps you need to define another private variable or remove one that is no longer used.

By reviewing the binary saved state and performing a little experimentation, we determined that the workflow serialization engine is finicky about matching the saved state to the assembly version and about code artifacts. The check for assembly version is done first, and the check for code artifacts happens next.

You may be tempted to not change the assembly version. This indeed causes the workflow to successfully pass the check for full strong name and version. However, the workflow engine then attempts to load the old persisted state by using the new assembly. If the new assembly contains any changes in code structure, the workflow engine is unable to load the persisted state because of the mismatch between saved and expected entities. In this situation, you can see IndexOutOfRange exceptions in the ULS logs and "Failed to run workflow" errors on the Workflow Status page. Therefore, you should always version your workflow assemblies whenever you make changes to the code layout and are ready to redeploy.

Each workflow association remembers the full strong name of the assembly with which it was registered. This means that if you deploy a new version of the workflow assembly, you need to also deploy a new version of Workflow.xml, re-register (deactivate and then activate) the workflow Feature, and recreate or add the workflow association so that Windows SharePoint Services can connect to the new version of workflow assembly.

The following sections discuss three ways in which you can upgrade and version a workflow.

Option 1: Move all content to new site and re-execute incomplete workflows

You can create a new SharePoint site, register the new version of workflow on that site, and migrate list or document items that have workflows associated with them, omitting completed workflows. For items that have workflows pending, re-execute those workflows from scratch.

This option isn't great, because it requires data migration between the old SharePoint site to the new SharePoint site. If your workflow fixes also modify the format of the underlying site (such as new fields in lists), the migration is nontrivial. Also, you have to re-execute your workflows that were not done, which adds burden on users who have already done their part to move workflow along.

Option 2: Code workflow so it can continue or restart all in-process workflows

For state machine workflows, you have an option of designing your state transition flow to accommodate possible future restarts. If your workflow process keeps some kind of state information in the list item or document properties, that state information is most likely a field in the list or document library. Your workflow probably expects a certain initial state value when it starts. A workflow that is already running would naturally have a different in-process state value.

If you code your workflow to accommodate any state value (not just the initial one) and during start-up jump to the code that executes that state, you can remove all completed and running workflows, remove the old workflow association with the old workflow assembly version, add a new workflow association with a new workflow version, and then re-execute all pending workflows.

Naturally, this approach puts a burden on you as a developer to add numerous conditional "re-entry/re-route" points to the beginning of your workflow. You may not want to do this extra work, or you may not be able to thoroughly test all re-entry scenarios. Also, this design is feasible for state machine workflows only.

Option 3: Run old workflows to completion with old assembly and new workflows with new assembly

You can change the mode of a workflow association to "No New Workflows," which stops new workflows from starting but allows existing ones to complete. By putting old workflow associations into "No New Workflows" mode and adding new workflow associations, you can have multiple versions of a workflow assembly running at the same time.

For example, you can have a workflow association named "MyCustomWorkflow v1.0.0.0" that is associated with version of your workflow assembly in the "No New Workflows" mode. You can then add "MyCustomWorkflow v1.0.1.0" associated with version of your workflow.

This is not the ideal solution either. For example, if the old workflow assembly has a bug that is not fixed, continuing to run the old workflow with the old assembly isn't helping you. Also, if the "On Item Change" event is enabled for the new workflow association, every time the item with the old workflow changes, it starts an instance of the new workflow. However, in some scenarios this approach remains the most valid.

Now, you need to deploy the new workflow assembly without removing the old version. Although stsadm.exe has the "upgradesolution" command, all it does is replace the old solution package with the new one. When the old solution package is removed, old workflow assembly will be removed from GAC as well.

To deploy a new workflow package over an old one
  1. Change the workflow assembly version.

  2. Change the workflow assembly version in the CodeBesideAssembly attribute of the Workflow element in Workflow.xml.

  3. Change the solution GUID in the manifest.xml file of your workflow project.

  4. Change the name of the .wsp solution package that is generated by package.ddfor rename the generated .wsp file.

  5. Deploy the new solution package alongside the old solution package, thus registering a new version of the workflow assembly in the GAC.

  6. Perform an IISReset command.

  7. Put the old workflow association into "No New Workflows" mode.

  8. Manually or programmatically create the new workflow association alongside the old workflow association, being sure supply a different name.