i:0#.w|Ali.Mazaheri

Azure, Open Source, SharePoint and ...

Sending a reminder in a sequential workflow

A common task in a workflow is to send a reminder email to a user, if he/she does not complete the task within a timeframe (e.g. one day). One way to implement this functionality in a Sequential Workflow is to leverage EventHandlingScope and Delay activities by following these steps:

  1. After CreateTask activity add an EventHandlingScopeActivity and put a WhileActivity along with an OnTaskChanged and UpdateTask as child activities and set the correlation token and other properties.

  2. In designer Switch to EventHandlingScope’s EventHandler view, add an EventDrivenActivity and a DelayActivity and a CodeActivity as child activities.

  3. Use SPUtility.SendEmail inside the CodeActivity to send a reminder email to requestor’s address.

 

Update (Here is a different approach for sending regular reminders using ConditionedActivityGroup (CAG), submitted by my colleague Daniel Odievich):

When developing SharePoint workflows, one often needs to send email notification for a task, and then keep sending regular reminders for this task at scheduled intervals. When developing workflows in SharePoint Designer, one can use DelayUntilActivity and DelayForActivity, but those were not designed for in Visual Studio workflows.

One solution already presented by Ali (http://blogs.msdn.com/alimaz/archive/2008/01/22/sending-a-reminder-in-a-sequential-workflow.aspx) is to use EventHandlingScopeActivity to wait for the task notification while using its contained EventDrivenActivity to queue up a delay using DelayActivity. Unfortunately, this approach limits you to one-time reminder as DelayActivity will only execute once, instead of regular reminders. If you try to wrap DelayActivity in a WhileActivity, the EventDrivenActivity won’t compile because its first child must be IEventActivity (such as DelayActivity).

A different solution is to use the ConditionedActivityGroupActivity (http://msdn.microsoft.com/en-us/library/bb675237.aspx) with OnTaskChanged in one group, DelayActivity in another group and a couple of code conditions to loop around them.

The pseudo-code structure of this design is:

<CreateTask />

<SendEmail Type=“First” />

<ConditionedActivityGroup UntilCondition=“Until_TaskComplete_Condition” >

<SequenceActivity Type=“Task” WhenCondition=“While_TaskNotComplete_Condition”>

<OnTaskChanged />

<UpdateTask />

</SequenceActivity>

<SequenceActivity Type=“Reminder” WhenCondition=“While_TaskNotComplete_Condition”>

<DelayActivity />

<SendEmail Type=“Reminder” />

</SequenceActivity>

</ConditionedActivityGroup>

<CompleteTask />

As long as the task is not done, Until_TaskComplete_Condition should be setting its e.Result to “false”. Meanwhile, While_TaskNotComplete_Condition should be setting its e.Result to “true”. Once the task is complete, Until_TaskComplete_Condition should be setting its e.Result to “true”, while While_TaskNotComplete_Condition should be setting its e.Result to “false”. In other words, the outcomes of these evaluations should be opposite values (Until_TaskComplete_Condition != While_TaskNotComplete_Condition).

When task is created, the following sequence of events will occur:

1. <CreateTask />

2. <SendEmail Type=“First” />

3. <ConditionedActivityGroup UntilCondition=“Until_TaskComplete_Condition” >

4. <SequenceActivity Type=“Task” WhenCondition=“While_TaskNotComplete_Condition”>

5. <OnTaskChanged /> – register

6. <SequenceActivity Type=“Reminder” WhenCondition=“While_TaskNotComplete_Condition”>

7. <DelayActivity /> – sleep

The workflow will then go to sleep and wait for OnTaskChanged event or for DelayActivity to elapse.

If OnTaskChanged event wakes up workflow, and task has not been completed, the OnTaskChanged group of activities will run to complete and be scheduled again because Until_TaskComplete_Condition will not evaluate to “false”. DelayActivity group of activities will still be waiting for delay to elapse. Following sequence of events will occur:

1. <OnTaskChanged /> – wake

2. <UpdateTask />

3. <ConditionedActivityGroup UntilCondition=“Until_TaskComplete_Condition” > – true

4. <SequenceActivity Type=“Task” WhenCondition=“While_TaskNotComplete_Condition”>

If OnTaskChanged event wakes up workflow, and task has been completed, the OnTaskChanged group of activities will run to completion. Because Until_TaskComplete_Condition will evaluate to “true”, the ConditionedActivityGroup will consider itself done, cancel execution DelayActivity group of activities and move to next step in workflow. Following sequence of events will occur:

1. <OnTaskChanged /> – wake

2. <UpdateTask />

3. <ConditionedActivityGroup UntilCondition=“Until_TaskComplete_Condition” > – false

4. <CompleteTask />

When DelayActivity elapse before the task is completed, the DelayActivity group of activities will run to completion and be scheduled again because Until_TaskComplete_Condition will not evaluate to “false”. The OnTaskChanged group of activities will still be waiting for Task Changed event. Following sequence of events will occur:

1. <DelayActivity /> – wake

2. <SendEmail Type=“Reminder” />

3. <ConditionedActivityGroup UntilCondition=“Until_TaskComplete_Condition” > – true

4. <SequenceActivity Type=“Reminder” WhenCondition=“While_TaskNotComplete_Condition”>

5. <DelayActivity /> – sleep

The workflow designer outline of this is shown below:

clip_image002

The OnTaskChanged branch of CAG

clip_image004

DelayActivity branch of CAG