3
votes

I develop a SharePoint workflow with a Replicator activity to replicate a custom activity for every approver. The custom activity implements an approval branch for a particular user. It has classic form with CreateTask, While, OnTaskChanged and CompleteTask activities.

I setup UntilCondition on the replicator to cancel execution after one approver chooses to reject the approval and then workflow finishes. The problem happens with other uncompleted tasks which "hang" in their current state. User does not see this state when open the task.

I put UpdateAllTasks after the replacator to set the task status to Cancelled. But since there is no event activities between CompleteTask (for the rejected task) and UpdateAllTasks, the UpdateAllTask activity set Cancelled for the rejected task also.

The question, what can I do to flush the pending change made by CompleteTask before UpdateAllTasks?

Or perhaps, there is another way to implement such workflow. I was thinking about the way to implement Cancel handler for the custom activity with UpdateTask. But I do not know how to implement it and tell to the cancel handler that it executes in the case of the rejection.

4

4 Answers

3
votes

After being faced with the same problem and spending a lot of time researching and trying out different options, I think I found a really good solution. I'm posting it here for posterity.

  1. Create a custom activity that extends SequenceActivity called ReviewActivity
  2. The ReviewActivity includes the typical CreateTask -> While -> OnTaskChanged -> CompleteTask scenario
  3. In my workflow, I have a Replicator that is creating many instances of the ReviewActivity (and thus many Tasks).
  4. The replicator implements an UntilCondition that checks to see if a task was rejected (this is set in the ChildCompleted)
  5. After the Replicator, I have an UpdateAllTasks to close the remaining tasks

If you have any experience with this scenario at all, you are getting ready to tell me that UpdateAllTasks also updates the originally rejected task since the "CompleteTask" has not been persisted to the database yet. The magic is in an attribute that you can define for the custom activity (ReviewActivity) called PersistOnClose.

[Designer(typeof(ActivityDesigner), typeof(IDesigner))]
[PersistOnClose]
public partial class ReviewActivity : SequenceActivity

This attribute ensures that once the ReviewActivity is complete, all changes are persisted to the database. Since the last activity in the ReviewActivity is the "CompleteTask", the task is saved to the DB. Therefore, the UpdateAllTasks will not touch it.

I hope this helps someone.

0
votes

Did you try putting a code activity between the complete task and updatealltasks activity?

0
votes

Struct your activities as the following: Create Task - > OnTaskChanged -> If/Else Activity (set the condition true if the approver decision was "reject") -> (Inside the If branch) UpdateAllTasks Activity (set the status to cancel within the Activity properties) -> (Outside the If branch) CompleteTask Activity.

When an approver decides to reject a task, the WF will cancel all the tasks. It will also cancel the task of the person who rejected but right after the "CompleteTask" activity will fire and set the corresponding task as Completed.

0
votes

I was faced with a similar problem today. I solved it with:

  1. setting variable taskCancelled to true inside ontaskchanged method if the task was cancelled (based on field completed and percentageCompleted)
  2. updateAllTasks method run to cancel every task after every ontaskchanged event (in a sequence inside replicator) only if taskCancelled variable was true
  3. setting until condition for replicator to true if taskCancelled variable was true