Developing Workflow Solutions with SharePoint Server 2007 and Windows Workflow Foundation

Summary: This article describes best practices for developing workflows for Microsoft Office SharePoint Server 2007 by using Microsoft Visual Studio. (23 printed pages)

Daniel Odievich, Senior Consultant, Microsoft Consulting Services

Ali Mazaheri, MCM SharePoint 2007

April 2008

Applies to:   Microsoft Office SharePoint Server 2007, 2007 Microsoft Office system, Windows SharePoint Services 3.0, Windows Workflow Foundation

Contents

  • Introduction to Developing Workflows

  • Using Windows SharePoint Services and SharePoint Server as a Workflow Platform

  • Understanding Windows Workflow Foundation

  • Using Windows Workflow Foundation in Windows SharePoint Services and SharePoint Server

  • Structuring a Workflow Project

  • Creating Workflow Associations by Using the SharePoint Object Model

  • Debugging Deployment Issues

  • Debugging Workflow Code

  • Setting Workflow Outcome Values

  • Starting and Resuming Workflows

  • Managing Configuration Settings for Your Workflows

  • Saving Workflow State and Upgrading Workflow Assemblies

  • Creating UI for Activities

  • Understanding Workflow Types: Sequential vs. State Machine

  • Developing Workflow Solutions with SharePoint Server

  • Conclusion

  • Additional Resources

Introduction to Developing Workflows

Windows SharePoint Services 3.0 and Windows Workflow Foundation offer to businesses a robust and rich workflow platform on which to model both simple and arbitrarily complex business processes. Microsoft Office SharePoint Designer 2007 offers code-free workflow design for simple business processes; experienced programmers can use Microsoft Visual Studio to create code-driven workflows that model sophisticated process flows and integrate with a variety of external systems.

This article explains how to build complex, code-driven workflows by using Visual Studio, offers best practices on workflow development, and provides samples of both sequential and state-machine workflow solutions based on real-world project experience.

Using Windows SharePoint Services and SharePoint Server as a Workflow Platform

Windows SharePoint Services 3.0 and Microsoft Office SharePoint Server 2007 are most often used for collaboration and content publishing. Both Windows SharePoint Services 3.0 and Office SharePoint Server 2007 can also serve as a platform for simple and complex business workflows, ranging from content approval for single documents to reviewing and editing a set of documents or exchanging information with external line-of-business (LOB) systems.

Windows SharePoint Services offers developers a workflow host on which to build content routing, approval, and other custom workflows by using familiar Visual Studio tools. Windows Workflow Foundation provides workflow runtime, and SharePoint lists and document libraries provide data repository for items used by workflows.

You can also use InfoPath Form Services to render custom Microsoft Office InfoPath 2007 forms in a browser to create workflow items, provide additional parameters to workflows, and display workflow tasks.

Understanding Windows Workflow Foundation

Windows Workflow Foundation (WF) is the programming model, engine, and tools for building workflow-enabled applications. With WF, you can visually model the flow of business process and separate the "plumbing" activity code from actual business flow. WF provides familiar programming building blocks such as if/else, parallel execution, calling Web services, and sending e-mail, as well as offering you the ability to execute any code. The WF engine provides workflow persistence services to enable long-running business processes that can span days, months, or years.

Using Windows Workflow Foundation in Windows SharePoint Services and SharePoint Server

In Windows SharePoint Services and SharePoint Server, you can associate any list or document library with one or more workflow types. Each item in the list or document library then has the ability to have an instance of that workflow type running on it. Although you can associate several workflows with a list or a document library, you can have only one instance per item at any given time. To exceed this limit, you can version your workflow assemblies, provide unique names for your workflows, and change the solution GUID of the workflow deployment packages. For more information on these techniques, see Saving Workflow State and Upgrading Workflow Assemblies later in this article.

You can register a workflow type to start manually through the UI of SharePoint Server or automatically when the item is created or changed. Workflow association is specific to a list or document library instance in a Web site. To complete workflow association, an instance of a Task list and Workflow History list are required (and can be automatically created if using the SharePoint Server UI). If you use site definition to provision the site, you can include the Workflow History list in elementManifest.xml by using following ListInstance element definition:

<Elements xmlns="https://schemas.microsoft.com/sharepoint/">
    <ListInstance FeatureId="00bfea71-4ea5-48d4-a4ad-305cf7030140" TemplateType="140" Title="Workflow History" Url="Lists/Workflow History"/>
</Elements>

SharePoint Server stores the workflow association data of a list or document libraryin the WorkflowAssociation table in the SharePoint content database. The WorkflowAssociation table stores the association between the workflow itself (using its workflow.xml GUID, which is stored in the BaseID column), the list or document library GUID, the task list GUID, the Workflow History GUID, the Web GUID, the auto-cleanup date of the Workflow History list, and the permissions.

Windows SharePoint Services 3.0 implements its own WF persistence database by using a slightly modified WF persistence service. A persisted workflow instance is created by using binary serialization, compressed to save space, and then stored in the InstanceData image column in the Workflow table in the SharePoint content database, along with metadata such as workflow instance GUID, workflow association GUID, list GUID, list item GUID, Web GUID, workflow persistence size, last activity date, and workflow status.

Workflow History and workflow task lists are regular SharePoint objects that are stored in the SharePoint content database with all other lists, list items, and documents.

A timer job deletes the Workflow History and associated Task items for a workflow instance that was completed more than 60 days ago. (The cleanup does not delete the persisted workflow instance of a completed workflow.) Although you can adjust the duration by using the SPWorkflowTemplate.AutoCleanupDays property, we recommend that you not modify this setting. A longer duration causes more events to accumulate in the history database, which slows performance.

If an item is deleted from a list or document library, all persisted workflow instances associated with the item—including tasks created by those workflows and workflow history list entries—are also deleted.

Structuring a Workflow Project

A workflow template in SharePoint Server is deployed as a SharePoint Feature. As with any Feature, deployment requires creating several XML files to describe the Feature, packaging them into a solution package (.wsp file), and deploying the package to SharePoint Server. A timer job then extracts the files, registers the workflow assembly in the global assembly cache (GAC), and propagates the rest of the files to their specified locations on each server in the server farm.

To define a workflow Feature and package it in a solution package, you create the following files:

  • Manifest.xml with a solution GUID; defines the layout in the .wsp file and references feature.xml, workflow.xml, and other supporting files

  • Feature.xml with feature name, description, version, Feature GUID; references workflow.xml

  • Workflow.xml with workflow name, description, version, workflow GUID, and the strong name of your workflow assembly

  • Package.ddf (directive definition file) with Makecab.exe instructions on how to package the Feature into the .wsp file; references all relevant files

The GUIDs in all the various files are important and are worth discussing in greater detail.

Note

Never reuse any SharePoint GUIDs that you find in the various built-in Feature.xml and site definition files. Always use Guidgen.exe to generate your own GUIDs.

The solution GUID defined in Manifest.xml acts as the unique key for the deployed .wsp solution package; the file name of the .wsp file acts as a backup unique key. A .wsp package with a given solution GUID or name can be deployed to SharePoint only once. If you recompile your project and repackage the .wsp solution package, you cannot redeploy it without removing the previously deployed package. This restriction becomes important when you want to upgrade your workflow assembly while leaving the previous assembly installed. In this scenario, you must need change both the solution GUID and the name of the .wsp solution package. For more detail, see Saving Workflow State and Upgrading Workflow Assemblies later in this article.

The Feature GUID defined in Feature.xml is the handle to your Feature. You can use the Feature GUID with various Stsadm.exe commands to install, activate, uninstall, and deactivate your Feature and with Onet.xml in a site-definition solution to automatically provision your Feature.

The workflow GUID defined in Workflow.xml is the handle to your workflow. It can be used to look up your installed workflow to create an SPWorkflowAssociation object to automate provisioning or to start an instance of a workflow programmatically. For convenience, we recommend creating a constant in your source file to contain this GUID.

For consistency, it helps to mirror (or at least closely resemble) the structure of the deployed Feature after it is deployed to the SharePoint installation directory (usually C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\template). For example, assume the following:

  • Your workflow Feature is named MyCustomWorkflow.

  • The workflow uses a custom ASP.NET page that acts as a form for your workflow.

  • The Workflow uses supporting XML files that contain lookup data.

  • You are using the SPFeatureReceiver class to configure your workflow

For this project, you can create the following project structure.

Path and file Purpose

\Properties AssemblyInfo.cs

Contains assembly information, such as version and copyright

\Template \Features \MyCustomWorkflow feature.xml

Describes the workflow Feature; refers to Workflow.xml

workflow.xml

Describes the workflow; provides a reference to the MyCustomWorkflow.cs class and the strong name of the resulting assembly

\Layouts \MyCustomWorkflow MyCustomWorkflowForm.aspx

A custom ASP.NET page for the workflow activity; packaged inside the .wsp solution package

\XML \MyCustomWorkflow MyCustomWorkflowSupportingFile.xml

Workflow-specific data; packaged inside the .wsp solution package

\Deployment manifest.xml

Describes the contents of the.wsp solution package after creation

package.ddf

Instructions for Makecab.exe to create the .wsp solution package from the source files in this project

\Workflow Provisioning Handler feature.xml

Describes the workflow provisioner Feature by using the SPFeatureReceiver class

FeatureProvisioning.cs

Code for the workflow provisioner SPFeatureReceiver class

CreateSolutionPackage.bat

Batch file to create the .wsp solution package

MyCustomWorkflow.cs

MyCustomWorkflow.designer.cs

MyCustomWorkflow.layout

MyCustomWorkflow.rules

Code for the workflow

Setup.bat

Batch file to install the .wsp solution package

MyCustomWorkflow.snk

Strong name key to sign the assembly

You can write a batch file that invokes Makecab.exe to process Package.ddf, and then you can call that batch file from the post-build event handler. Optionally, you can also automatically deploy or redeploy the package by using Stsadm.exe commands after a successful build. Or you can use a tool included with Visual Studio 2008 to package the solution.

Creating Workflow Associations by Using the SharePoint Object Model

The workflow association process consists of three distinct steps:

  1. Deploy the workflow Feature containing Workflow.xml and the workflow assembly to all front end Web servers by using the solution package.

  2. Activate the workflow Feature in the top-level site collection, a top-level site, or a subsite.

  3. Create an association between a list, document library, or content type and the workflow Feature, specifying options for workflow initiation and update.

The last step of associating an installed workflow with a list or document library can be tedious if it must be performed many times. For example, if you use a custom site definition to provision a subsite to enable collaboration and approval of a complicated document, consider including the workflow provisioning process by using the SPFeatureReceiver.FeatureActivated event and performing the following steps programmatically:

  1. Create an SPList object that references a list or document library that hosts the items used by the workflow.

  2. Create an SPList object that references an approval task list.

  3. Create an SPList object that references the Workflow History list.

  4. Create an SPWorkflowTemplate object by using the workflow GUID from Workflow.xml.

  5. Create an SPWorkflowAssociation object that connects the SPWorkflowTemplate object with the SPList objects for the approval task list and the Workflow History list.

  6. Add an SPWorkflowAssociation object to the SPList object you created in the first step.

You can include the following code in a custom Feature receiver assembly to implement the preceding steps.

public void ProvisionWorkflow(SPWeb web)
{
    using (web)
    {
        //Bind to lists.
        SPList approvalsList = web.Lists["Your Approval List Name Goes Here"];
        SPList approvalTasksList = web.Lists["Task List"];
        SPList workflowHistoryList = web.Lists["Workflow History"];
        
        //92EF6E3C-8DC1-41bc-AE1D-C5A04A06E028 is the workflow GUID
        //from workflow.xml\<Elements>\<Workflow>\[ID].
        SPWorkflowTemplate workflowTemplate = 
          web.WorkflowTemplates[new Guid("92EF6E3C-8DC1-41bc-AE1D-C5A04A06E028")];

        //Create workflow association.
        SPWorkflowAssociation workflowAssociation = 
          SPWorkflowAssociation.CreateListAssociation(workflowTemplate, 
          workflowTemplate.Description, approvalTasksList, 
          workflowHistoryList);

        //Set workflow options.
        workflowAssociation.AllowManual = true;
        workflowAssociation.AutoStartChange = true;
        workflowAssociation.AutoStartCreate = false;

        //Add workflow association.
        SPWorkflowAssociation workflowAssociationInList = 
          approvalsList.AddWorkflowAssociation(workflowAssociation);
    }
    return;
}

Debugging Deployment Issues

If your package deployment contains no custom code, debugging any deployment problems means analyzing the unified logging system (ULS) logs. To aid your search through the logs, raise the logging level of the Feature Infrastructure and Topology categories to "Verbose."

If your workflow package uses a custom class deriving from SPFeatureReceiver to configure itself during package deployment or removal, you may need to debug it. The easiest way to debug is to avoid attaching a debugger altogether and instead instrument your event-handling code to log to event logs, a flat file, or the ULS logs.

However, if you do decide to use a debugger, you need to attach to the host process that runs your SPFeatureReceiver object. Depending on circumstances, the host process can be W3wp.exe hosting the Central Administration site, W3wp.exe hosting your site, or Stsadm.exe. If you activate the Feature by using Stsadm commands, you can configure Stsadm.exe as the debug startup process with appropriate command-line parameters in the Visual Studio project properties.

Test-driven development with Visual Studio 2008 provides another great option to debug your code. The Visual Studio 2008 test host process can host unit tests that drive your SPFeatureReceiver object. Because SPFeatureReceiver event handlers expect a specially constructed SPFeatureReceiverProperties object, you should separate your provisioning code into a separate routine.

namespace MOSSWFSample.Workflow.StateMachine
{
    public class StateMachineWorkflowFeatureReceiver: SPFeatureReceiver
    {

        [SharePointPermission(SecurityAction.LinkDemand, 
          ObjectModel = true)]
        public override void 
          FeatureActivated(SPFeatureReceiverProperties properties)
        {
            if (properties == null)
            {
                return;
            }

            this.OnActivated(properties);
        }

        public void OnActivated(SPFeatureReceiverProperties properties)
        {
            SPWeb web = (SPWeb)properties.Feature.Parent;
            ProvisionWorkflow(web);
        }

        public void ProvisionWorkflow(SPWeb web)
        {
            //Provision your workflow as you see fit.
            ...
        }
    }
}

In the preceding example, your provisioning code requires only a simple SPWeb object pointing to the site. You can then write the following unit test and execute it by using Visual Studio 2008 Test Manager, which will automatically attach the debugger.

namespace MOSSWFSample.Workflow.StateMachine.UnitTest
{
        [TestMethod()]
        public void ProvisionWorkflowTest()
        {
            StateMachineWorkflowFeatureReceiver target = 
              new StateMachineWorkflowFeatureReceiver();

            SPSite site;
            SPWeb web;
            //Bind to the site where you are testing provisioning code.
            site = new 
              SPSite("http://developmentserver/sites/workflowtest");
            using (site)
            {
                web = site.RootWeb;
                using (web)
                {
                    target.ProvisionWorkflow(web);
               }
            }

            Assert.IsTrue(true, 
              "ProvisionWorkflow did not throw any exceptions.");
        }
    }
}

Debugging Workflow Code

To debug your workflow code, you should increase the level of logging to ensure that you can see all applicable activity. You also should instrument your code thoroughly. Then you can attach your debugger. The following sections discuss each task in more detail.

Increase the Logging Level

As with any SharePoint components, WF logging uses the Windows SharePoint Services Tracing service (SPTrace) to write to the ULS logs. If the workflow engine fails cannot start your code, the error messages appear in the logs.

To configure logging, go to Central Administration, click Operations, click Diagnostic Logging, and select one of the logging categories:

Workflow logging category Purpose

Workflow Features

Workflow.asmx web service activity ("Workflow Soap")

Workflow Infrastructure

All other workflow services activity (such as successful orunsuccessful event delivery and exceptions)

When you develop workflow solutions, we recommend that you raise the level of logging for Least critical event to report to the trace log to "Verbose."

Instrument Your Code

Instrument your workflow as much as you can: state transitions, event-driven activity, task creation and completion, e-mail generation, fault handling—everything. You can log events to event logs, flat files, databases (for example, by using Microsoft Enterprise Library), and, of course, the Workflow History list. Think carefully about what to log because the more information you provide about how your code is running, the easier your code is to debug in development and manage in production environments.

Debug Your Code

To debug workflow, attach to W3wp.exe process running the SharePoint site that is hosting your workflow. (In Visual Studio 2008, select Workflow as the value for Attach To.)

If your breakpoints are not being hit in Visual Studio 2008, click Tools and click Options to display the Options dialog box. Expand the Debugging and General nodes, and clear the Require source files to exactly match the original version option. (The workflow assembly runs from the GAC, and it doesn't have the PDB files with debugging information.)

When you debug your workflow, you do not need to redeploy the entire workflow Feature. Simply redeploying the workflow assembly to GAC and resetting Internet Information Services (IIS) enables you to start new workflow instances with new code. Also, if you haven't changed the code structure of the , you can continue to run previously started workflow instances.

Setting Workflow Outcome Values

By using the SharePoint object model, you can add your own workflow status that appears in the workflow association column. Each workflow status value corresponds to an integer that represents its place in a zero-based ordered list of column values. The first 15 values in the Microsoft.SharePoint.Workflow.SPWorkflowStatus enumeration are reserved by Windows SharePoint Services for internal use.

To add a custom status value, define a new StatusColumn element under ExtendedStatusColumnValues in Workflow.xml. The first status column value you specify is assigned the integer value of 15; the second status value, 16; and so on.

<ExtendedStatusColumnValues>
  <StatusColumnValue>Rejected</StatusColumnValue>
  <StatusColumnValue>Failed Verification</StatusColumnValue>
</ExtendedStatusColumnValues>

The maximum workflow status value that is reserved by Windows SharePoint Services is represented by SPWorkflowStatus.Max. Therefore, to set the workflow status to the first custom workflow value you defined, set the State property to SPWorkflowStatus.Max + 1.

private void setState_MethodInvoking(object sender, EventArgs e)
{

    if (!this.changeApproved)
    {
        //Set workflow status to Rejected
        this.workflowState = (int)SPWorkflowStatus.Max;
    }
    else
        //Set workflow status to Failed Verification
        this.workflowState = (int)SPWorkflowStatus.Max + 1;
}

Starting and Resuming Workflows

You can associate workflows with any SharePoint list or document library.A workflow can be:

  • Started on demand by a user by using the SharePoint UI (Start Workflow). If you are using InfoPath forms for activity pages, you can register a custom workflow initialization form.

  • Started or resumed in response to the On Item Created event for an item in a list or document library.

  • Started or resumed in response to the On Item Changed event for an item in a list or document library.

  • Started or resumed programmatically through the SharePoint object model.

  • Started programmatically through the Workflow Web service.

You should understand which process hosts your workflow in each of these scenarios:

  • The most common hosting process is W3wp.exe, the worker process for the IIS Web application hosting the SharePoint site with your workflow.

  • If your workflow uses DelayActivity or if a noncatastrophic problem occurs when the workflow instance starts, the host process is Owstimer.exe (Windows SharePoint Timer Service).

  • When an external application (such as a console application, Web service, or Windows Communication Foundation service) creates items programmatically through the SharePoint object model, the process running the code becomes the workflow host.

Special considerations apply to workflow initiation for host processes other than the W3wp.exe worker process. If you enable the On Item Created option during workflow association, the workflow host is the process that invokes the SharePoint object model to create that item. If you do not enable On Item Created, the user must start the workflow manually, or you must start it programmatically.

When code that runs outside the W3wp.exe process creates items by using the SharePoint object model, the workflow always fails to start, with the message "Workflow failed to start (retrying)" displayed on the Workflow Status page. Review of ULS logs reveals the following ArgumentException from deep inside the workflow activation code.

RunWorkflow: System.ArgumentException: Value does not fall within the 
expected range.
at Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties..ctor(
SPWorkflow workflow, Int32 runAsUserId, String associationData, 
String initiationData) 
at Microsoft.SharePoint.Workflow.SPWinOEWSSService.MakeActivation(
SPWorkflow workflow, SPWorkflowEvent e)
at Microsoft.SharePoint.Workflow.SPWinOeEngine.RunWorkflow(
Guid trackingId, SPWorkflowHostService host, SPWorkflow workflow, 
Collection`1 events, TimeSpan timeOut)
at Microsoft.SharePoint.Workflow.SPWorkflowManager.RunWorkflowElev(
SPWorkflow originalWorkflow, SPWorkflow workflow, Collection`1 events, 
SPRunWorkflowOptions runOptions)

As implied by "(retrying)," a Windows SharePoint Services timer service job (which is scheduled to run every five minutes) attempts to start this workflow. If you wait five minutes, you are successful in starting your workflow. So what is happening?

The code inside the SPWorkflowActivationProperties constructor checks whether the workflow is being started with the "System" SharePoint account process identity, which in this case is the AppPool account of the site that hosts SharePoint Server—not the SYSTEM Windows user account. Because the object-model code executes with a user identity that is not "System," a blank ArgumentException is thrown. Meanwhile, because all Windows SharePoint Services timer service jobs run under the "System" identity, the identity check succeeds and the process starts. Wrapping the code block that creates an item or starts the workflow programmatically by using the SPWorkflowManager.StartWorkflow method within the SPSecurity.RunWithElevatedPrivileges{delegate()} block does not work because the SPWorkflowActivationProperties class ignores elevation.

So how can you start a workflow outside the SharePoint Server hosting process when you do not want or are unable to use the account that is used for the AppPool identify of the SharePoint site that hosts your workflow? Use the Workflow Web service (_vti_bin/workflow.asmx) to start workflows.

The Workflow Web service offers limited functionality compared to the Microsoft.SharePoint.Workflow namespace of the SharePoint object model. The Web service does, however, let you start the workflow if you know the item URL and the workflow association GUID (which you can easily infer by using the workflow GUID that you define in Workflow.xml).

//92EF6E3C-8DC1-41bc-AE1D-C5A04A06E028 is workflow GUID from 
//workflow.xml\<Elements>\<Workflow>\[ID].
//The workflow is already associated with the list.
SPWorkflowAssociation associationTemplate = 
  list.WorkflowAssociations.GetAssociationByBaseID(new 
  Guid("92EF6E3C-8DC1-41bc-AE1D-C5A04A06E028"));

WorkflowService.Workflow workflowService = 
  new WorkflowService.Workflow();
workflowService.Url = 
  "http://developmentserver/sites/workflowtest/_vti_bin/Workflow.asmx";
workflowService.UseDefaultCredentials = true;
workflowService.Credentials = 
  System.Net.CredentialCache.DefaultCredentials;
workflowService.PreAuthenticate = true;
XmlDocument workflowParameters = new XmlDocument();
workflowParameters.LoadXml(
  "<workflowData>Any workflow parameters go here</workflowData >");
//listItem is SPListItem object referring on list item in the list.
workflowService.StartWorkflow(
  "http://developmentserver/sites/workflowtest/" + listItem.Url, 
  associationTemplate.Id, workflowParameters);

After workflow has started, it can be resumed by an On Item Changed event or programmatically by using the SharePoint object model from any host and any process account (if the account has permissions to the SharePoint databases and SharePoint site) without any reactivation problems regardless of host process. The check for the "System" identity is done only at initial startup of the workflow, not when the workflow is reactivated.

Managing Configuration Settings for Your Workflows

Because several processes can act as workflow hosts, if your workflow relies on configuration settings that are stored in the <appSettings> section in Web.config, you must accommodate the various locations where those settings must be replicated. Other settings that you might have include Windows Communication Foundation configuration endpoints, database connection strings, Web service URLs, and Enterprise Library configuration sections.

Workflow host process Name and location of settings file

W3wp.exe process hosting SharePoint site

Web.config in directory for SharePoint Server Web application, replicated on all front-end Web servers.

Windows SharePoint Services timer service (Owstimer.exe)

App.config for Owstimer.exe located in C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\bin\OWSTIMER.exe.config.

Your console or Windows Forms test application

App.config in same directory as your executable. For example, the configuration file for testworkflow.exe is testworkflow.exe.config.

Your Web or Windows Communication Foundation service

Web.config in directory for Web or Windows Communication Foundation service, replicated on all servers that are hosting your service (if applicable).

One alternative to replicating settings throughout these files is to store them in a standalone .config file in a well-known location (such as the FEATURES folder for your workflow Feature) and load it manually by using classes in the System.Configuration namespace. Although viable, this approach nevertheless has two major drawbacks:

  • It requires more code to load settings from appSettings, connectionStrings, or Windows Communication Foundation service configuration elements in an external file.

  • The file still needs to be replicated between front-end Web servers.

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 1.0.0.0 of your workflow assembly in the "No New Workflows" mode. You can then add "MyCustomWorkflow v1.0.1.0" associated with 1.0.1.0 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.

Creating UI for Activities

In SharePoint Server, users interact with a workflow by using two kinds of activity forms:

  • InfoPath forms (rendered by InfoPath or Microsoft Office Forms Server 2007) deployed inside a form library on a SharePoint site

  • Custom ASP.NET forms deployed on each front-end Web server and exposed to a SharePoint site from inside the _layouts virtual directory

InfoPath Forms and Office Forms Server

InfoPath forms and Form Server provide a robust platform for creating and deploying business forms throughout an enterprise. By using built-in features such as data connections and form controls, you can easily create a complicated form with a familiar user experience for internal or external line-of-business applications without writing a single line of code.

Note

You can also extend InfoPath form features by coding against the InfoPath API and using Microsoft Visual Studio Tools for Applications, but those subject are beyond the scope of this article.

Applications in the Microsoft Office 2007 system, such as Microsoft Office Word and Microsoft Office Outlook, are fully integrated with InfoPath and can also host InfoPath forms so that the user can update tasks without leaving the application.

SharePoint Server provides an extensible platform to create custom InfoPath forms for updating tasks when the built-in UI does not meet the business requirements.

Note

A Microsoft Office SharePoint Server Enterprise license is required to use the InfoPath forms render-item form that captures data to initiate custom workflows. You do not need an Enterprise key to render InfoPath forms for workflow tasks.

When designing a task form by using InfoPath, a secondary connection (MetaData.xml) needs to be added to the form as a secondary data source. Each control should be databound to its respective element in an XML file.

<?xml version="1.0" encoding="utf-8" ?>
<z:row xmlns:z="#RowsetSchema" ows_taskInstructions="" ows_taskComments="" ows_taskStatus="" />

In workflow code, you can access elements that are registered through MetaData.xml by using the SPTaskProperties.ExtendedProperties collection.

To register InfoPath forms with a workflow, add their URN entries (<Task0_FormURN>, <Task1_FormURN>, and so forth) in the Workflow.xml file.

<Task0_FormURN>
  urn:schemas-microsoft-com:office:infopath:SampleApprovalTask:-myXSD-2007-06-05T03-26-26
</Task0_FormURN>
<Task1_FormURN>
  urn:schemas-microsoft-com:office:infopath:SampleChangeManagementTask:-myXSD-2007-08-01T20-18-49
</Task1_FormURN>

To use the forms in a task library, you specify which form replaces the built-in task UI by setting the TaskType property of SPWorkflowTaskProperties to the zero-based index of the form as defined in Workflow.xml—for example, 0 for Task0_FormURN and 1 for Task1_FormURN.

private void CreateChangeManagementTask_MethodInvoking(object sender, 
  EventArgs e)
{
    //Creates a new Task for Change Request Change management.
    changeManagementTaskID = Guid.NewGuid();

    changeManagementTaskProperties = 
      new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
    changeManagementTaskAfterProperties = 
      new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
    changeManagementTaskBeforeProperties = 
      new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
    changeManagementTaskProperties.TaskType = 
      1; //Specifies form with URN in <Task1_FormURN>
    changeManagementTaskProperties.Title = 
      string.Format("New Change Request: {0} for deployment", 
      workflowProperties.Item.Title);
    changeManagementTaskProperties.AssignedTo = 
      changeRequest.changeManagementGroup;
    changeManagementTaskProperties.Description = 
      string.Format("Please schedule this Change Request: {0} for " + 
      "deployment. Mark Task complete when change has been completed.", 
      workflowProperties.Item.Title);
    changeManagementTaskProperties.ExtendedProperties["taskInstructions"] = 
      string.Format("Please schedule this Change Request: {0} {1}, {2} " + 
      "for deployment. Mark Task complete when change has been completed.", 
      workflowProperties.Item.Title, workflowProperties.Originator, 
      itemChangeRequest);
    changeManagementTaskProperties.SendEmailNotification = true;
}

Custom ASP.NET Forms Under the _layouts Directory

You can also use ASP.NET forms to interact with workflows. In Windows SharePoint Services 3.0 and SharePoint Server 2007, ASP.NET forms are simply ASP.NET pages, a basic building block of any Web application that is familiar to every developer who uses Microsoft .NET Framework.

You may have noticed how many SharePoint administrative pages are served from the _layouts virtual directory. You can also put your own ASP.NET pages under this virtual directory; when served, they act as part of your SharePoint site, inheriting SharePoint page context and all other SharePoint plumbing.

When your custom ASP.NET workflow is part of your SharePoint site, the workflow can interact with the site either programmatically by using the SharePoint object model or indirectly by modifying workflow item properties, triggering the "On Item Changed" event, and manipulating workflow execution.

When developing ASP.NET forms, you have a choice of either putting all your logic as inline code or utilizing code-behind files (.aspx.cs or .aspx.vb). The following table outlines the deployment options you should consider.

Type of ASP.NET activity-page coding Development and deployment considerations

All code inline in .aspx files

  • Not as much IntelliSense support.

  • Can use ASP.NET Web project for development.

  • Only .aspx files must be deployed, compiled on the fly by ASP.NET.

Code-behind files

Deployment of both .aspx and .aspx.cs/.aspx.vb code-behind files

  • Full IntelliSense support.

  • Can use ASP.NET Web project for development.

  • Deployment of all files together allows pages to be compiled on the fly by ASP.NET.

Code-behind files

Deployment of all code-behind code as a compiled assembly

  • Full IntelliSense support.

  • Cannot use ASP.NET Web project for development.

  • To use your custom site assembly, it must be signed, registered in the GAC and registered as safe in Web.config. This means that all code in code-behind files must come from a Visual Studio class library project, not from an ASP.NET Web project.

To expose an ASP.NET form under the _layouts virtual directory, first create an ASP.NET page, with or without code. Then deploy the form to front-end Web servers by using a solution deployment package. The final destination for the .aspx and code-behind files is a subdirectory under C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS (for example, C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\MyCustomWorkflow\MyActivityPage.aspx).

You can now access your ASP.NET page from _layouts/MyCustomWorkflow—for example, http://developmentserver/sites/workflowtest/_layouts/MyCustomWorkflow/MyActivityPage.aspx.

Understanding Workflow Types: Sequential vs. State Machine

WF supports both sequential and state-machine workflows. Your choice depends on the tasks you need to accomplish.

Sequential Workflow Use

A sequential workflow executes a series of predefined steps to accomplish a task. A sequential workflow is excellent for a business process that can be completed by using a well-defined path of execution, without much recursion, retracing of steps, or unexpected departures. For example, a sequential workflow can work well for the following:

  • Expense report with several layers of approval

  • Change-management application with subsequent approvals by Dev, QA, IT, and Ops

  • Helpdesk application with escalation through tiers

  • Processes in which the graphical representation is shown to non-developers

State-Machine Workflow Use

State-machine workflows work in event-driven scenarios. A state-machine workflow contains two or more states, with one state being the active state at any given time. Each state can represent an assignment of business task; any state can transfer to any other state. A state-machine workflow can model complex, nonlinear, branching business process interactions that have more than simple true/false or approve/reject outcomes. For example, a state-machine workflow can work well for the following:

  • Complex sales-quote generation for an advanced manufacturing process, in which many reviewers and approvers must review specifications (some more than once) and the quote can go through several rejections and reviews before being approved.

  • Large real-estate contract, in which multiple documents might be independently tracked by individual workflows; together they constitute a proposal which can go through legal review, contract negotiation, modification, and escrow before being signed and archived.

Choosing a Workflow Model in Visual Studio

With Visual Studio 2008 extensions for .NET Framework 3.x (Windows Workflow Foundation) installed, the Add New Item dialog box in Visual Studio offers the following options for new workflows:

  • Sequential Workflow (code)   Sequential workflow with definition and user code in same code file.

  • Sequential Workflow (with code separate)   Sequential workflow with definition expressed as XAML and user code in a separate code file.

  • State Machine Workflow (code)   State-machine workflow with definition and user code in same code file.

  • State Machine Workflow (with code separate)   State-machine workflow with definition expressed as XAML and user code in a separate code file.

Regardless of the workflow type you use, you always package workflow into a SharePoint Feature.

Developing Workflow Solutions with SharePoint Server

The best way to develop WF solutions with SharePoint Server is to use a physical or virtual machine that runs Windows Server 2003, SharePoint Server, and all developer tools. The developer machine must contain the following:

  • Windows Server 2003 or Windows Server 2003 R2

  • Windows Server 2003 Service Pack 2

  • Web Role (ASP.NET) enabled

  • .NET Framework 2.0 SP1

  • .NET Framework 3.0 SP1

  • Windows SharePoint Services 3.0 and SharePoint Server 2007

  • Windows SharePoint Services 3.0 Service Pack 1 and SharePoint Server 2007 Service Pack 1

  • Any post-SP1 SharePoint updates to Windows SharePoint Services and SharePoint Server

  • All Windows Server 2003 and SharePoint Server 2007 hotfixes from Windows Update or Microsoft Update

  • Visual Studio 2008 with Visual Basic, C#, and Web Development installed
    OR
    Visual Studio 2005 with extensions for .NET Framework 3.0 (Windows Workflow Foundation) and Visual Studio 2005 extensions for Windows SharePoint Services 3.0

You might want to host databases on another server even when configuring a standalone development environment. However, if you do decide to host databases on the same development server, you must also install the following:

  • Microsoft SQL Server 2005

  • SQL Server 2005 Management Components

  • SQL Server Service Pack 2

  • SQL Server Cumulative Update 5 or later

Other handy tools to ease development are:

  • .NET Framework SDK tools

  • Platform SDK tools

  • Windows webugging tools

  • Windows Resource Kit tools

  • Windows support tools

  • SysInternals utilities

  • .NET Reflector

  • File comparison tools (such as WinDiff)

  • A compression utility (such as WinZip) to look inside WSP and STP packages

Although all components of SharePoint Server can run on one server, it is rarely configured that way in production environments. Whenever you develop solutions for SharePoint Server, you should always have a set of development and test environments that closely resemble the final production configuration. Doing so can save you trouble as you discover the details of complex interactions between servers in various SharePoint roles during your solution development.

One common way of emulating a production environment is to build it by using Microsoft Virtual PC or Microsoft Virtual Server, creating a set of machines that are joined to your company’s domain or deployed in a test domain.

For example, you need the following set of virtual machines to deploy a domain controller, e-mail server, database server, development server, medium-sized test farm, and a client test machine:

  • 1 domain controller/DNS/DHCP server

  • 1 development server (application/index/front-end Web/query/database/POP3/SMTP server with all development tools)

  • Test Farm:

    • 1 database server

    • 1 application/index server

    • 2 front-end Web/query servers

    • 1 e-mail server (running Microsoft Exchange Server or Windows with POP3/SMTP)

    • 1 client workstation (Windows XP or Windows Vista with Office) to test Web site access

Developing with Visual Studio 2005

When Microsoft release Windows SharePoint Services and SharePoint Services, Visual Studio 2005 was the only option for development. Now that Visual Studio 2008 is available, it offers improved development experience that is superior to Visual Studio 2005. The ability of Visual Studio 2008 to target different versions of .NET Framework makes it possible to mix and match SharePoint projects such as site definitions and Web Parts (.NET Framework 2.0) and workflow (.NET Framework 3.0 and 3.5) in the same solutions.

Nevertheless, the Visual Studio 2005 is still a viable tool to use for SharePoint development if you haven't switched to Visual Studio 2008 yet.

Conclusion

With SharePoint Products and Technologies, you can create workflows for both simple and arbitrarily complex business processes. This article discussed the ways in which experienced developers can use Visual Studio to create code-driven workflows that model sophisticated process flows and integrate with a variety of external systems.

Additional Resources