Our client through an interesting curve ball at us midway through my workflow project. We were probably 70% code complete, when it was realised the design had no provision for a certain scenario. The workflow is triggered by a user completing an InfoPath form - this has many input controls to collect data, and several of them are dropdowns which dictate the particular path through the workflow which occurs. One such dropdown looks something like:
- type 1
- type 2
The value selected is evaluated by IfElse conditions in the workflow - if "type 1" is chosen, we go down a particular branch, if "type 2", a different branch and so on. Now for this particular dropdown, we realised we hadn't specified what should happen for the "both" condition (that's agile for you!). The new requirement which arose was that notifications and tasks should go to to two teams (i.e. both of them), and after further discussion we decided it would be appropriate to kick off two separate workflows, one for each team.
Most workflow frameworks can deal with this by using 'child' workflows, and Workflow Foundation is no different. After a couple of hours refactoring the implementation to use this approach, my proof-of-concept looked like:
So whilst the 'InvokeWorkflow' activities are closed (i.e. the child workflows aren't expanded), you can hopefully see that if we go down the branch on the left, we launch one child workflow, but in the branch on the right (for "both"), we launch two child workflows in parallel.
I tried a couple of things, the image above shows using a sequential parent workflow (though the child workflow is state-machine), but couldn't get this to work. Using the debugger I could see that the child workflows were being initialized - I could step through InitializeComponent(), but breakpoints in 'OnWorkflowActivated' were never hit - the event wasn't raised. The SharePoint log showed:
System.Workflow.Activities.EventDeliveryFailedException: Event "OnWorkflowActivated" on interface type "Microsoft.SharePoint.Workflow.ISharePointService" for instance id "67c4929a-9280-4b8b-9a4c-a1b85b04267c" cannot be delivered. ---> System.InvalidOperationException: Event Queue operation failed with MessageQueueErrorCode QueueNotFound for queue 'Message Properties Interface Type:Microsoft.SharePoint.Workflow.ISharePointService Method Name:OnWorkflowActivated
So I became suspicious of whether it's possible to use InvokeWorkflow in a SharePoint workflow. Microsoft haven't yet got back to me on this, and the most detailed SharePoint workflow book I'm aware of (Workflow in the 2007 Microsoft Office System by David Mann) does not discuss InvokeWorkflow.
Time for some lateral thinking.
We know that we will definitely have two workflows if two completed InfoPath forms are added to the document library, so I asked the client what they thought of getting the system to split a "both" request into two separate items - this would mean having two separate InfoPath forms in the list. The answer was "actually that's probably better for the users come to think of it", so the solution I came up with was:
- a list event receiver which executes before the workflow - this does the following:
- opens the binary contents of the InfoPath form as a string, and modifies the dropdown value from "both" to "type 1"
- creates a duplicate of the original file in the same library, but modifies the dropdown value here from "both" to "type 2"
- running some ad-hoc code to reorder the event receivers on the list - we need to split the item before workflow starts (so we get two workflows) but SharePoint adds the SPWorkflowAutostartEventReceiver at sequence of 1 so it executes first. This code switches the event receiver sequence so our pre-processing happens first.
On the last point, I considered that the SharePoint team may have set workflow to execute first for a reason, but then again it's just another event receiver and SharePoint does not prevent this reordering (in the same way it does with some other changes, such as deleting the welcome page from a web for example). In any case, we've tested pretty extensively and the solution hasn't missed a beat.
In case it's useful, the following code can be used to duplicate and modify a file - this could be used in a list receiver (as in my case) or in ad-hoc code. I used simple string manipulation to perform the modification since InfoPath forms are just XML, and also because using XML-handling methods caused issues with the 'proprietary' InfoPath declarations. Simply replace the string replacement line with your own processing.
P.S. If anyone is using InvokeWorkflow in a SharePoint workflow, leave a comment :-)