The constraint of only allowing a single instance of a given workflow to run at a time on a list item may run right up against a real-world scenario in which multiple concurrent instances may be required.
The following workaround may help you resolve this problem. On the face of it, its really quite simple: dynamically generate additional workflow associations on the list as needed.
High level explanation: A workflow is associated with a list, making the workflow available to that list. There’s nothing stopping you from creating multiple associations of the same workflow, and therefore running multiple instances of that workflow concurrently, which each running under a different association.
OOB, you can do this in the UI – creating mulitple associations – as many times as you think your users will need. The weakness here is that the user will need to understand that the multiple workflows they see available to them are actually just different associations of the same workflow. Many users might find this confusing, creating more problems than it solves. Luckily, you can also generate these associations programmatically.
<Digression>
In other postings, I have discussed the idea of using a non-standard way to start workflows. Some benefits include ease of support for new versions of the workflow and more control over how the user interfaces with the workflow.Another benefit would be providing the user with a single point of interaction for starting the workflow, and then programmatically handling the logic of either using an available association or generating a new one.
</Digression>
Example: You have a simple workflow called "MyWorkflow" associated with a list called "MyList". A user clicks your custom "Start MyWorkflow" link for an item in MyList, and your custom ‘start workflow’ code kicks in and determines that the single association MyList has for MyWorkflow is currently being used. The code then generates a second association of MyWorkflow on MyList, and starts the workflow using that association. Later on, the instance running on the first association completes. After that, a user starts MyWorkflow on that same item. Your code then determines that there is an existing available association, and starts MyWorkflow using that association.
Notice that ideally the code only creates new associations when it has to – only when all the available associations are being used. Otherwise it will use the first available association it finds.
To get the collection of associations on the list:
SPWorkflowAssociationCollection WfAssociations = theItem.ParentList.WorkflowAssociations;
To determine if any association is available, loop thru this collection, only looking at associations with the BaseTemplate.Id value corresponding to the workflow you’re interested in.
To test an association for availabitlity, loop thru the workflows on the current item, and check the status:
if (currentWF.ParentAssociation.Id == wfAssociation.Id && currentWF.InternalState == SPWorkflowState.Running)
If needed, you can generate a new association:
SPWorkflowTemplate wfTemplate = Web.WorkflowTemplates[wfTemplateId];SPWorkflowAssociation newListAssociation = SPWorkflowAssociation.CreateListAssociation(wfTemplate, associationName, taskListName, historyListName);MyList.AddWorkflowAssociation(newListAssociation);
Where wfTemplateId is the Template ID of your workflow, and associationName is a uniquely generated name for the new workflow association. Something like the original workflow association name, with an appended _1, _2, _3, etc.
Happy coding, and I hope you find this workaround useful.