Wednesday, September 1, 2010

SharePoint Workflow – Parallel Replicator

The Problem

I recently was tasked with creating a SharePoint workflow for an internal document approval process. One of the major aspects of this workflow was that any number of tasks could be created depending on information filled out in an InfoPath form. In other words, the InfoPath form contains a repeating table where the user can enter N number of rows and I have to be able to create a task for each item in the submitted form. The logical choice for accomplishing this task was to use a Workflow Replicator activity. I fleshed out a workflow structure as follows:

clip_image001

When the replicator was initialized I would populate the InitialChildData property with my list of items that I needed to create and then in the OnChildInitialized method of the replicator I would set the properties on the CreateTask activity to create the appropriate task. I should also note that my CreateTask, TaskChanged and CompleteTask activity all have the same CorrelationToken with the correlation toke owner set to the sequence. This ensures that the replicator can create unique instances of the token. Everything was working as expected when the replicator ExecutionType was set to Sequence. When I changed the ExecutionType to parallel, the workflow would create N number of tasks but they would all be identical. It never failed that every task created would be the task that should be created for the last index of my InitialChildData collection. I am fairly new to SharePoint workflow development so I don’t know if my solution is the best practice or not but this is how I solved it.

The Solution

My solution was to write a custom SequenceActivity. This is nothing more than a class file that inherits SequenceActivity. My custom sequence activity did nothing more than register a DependencyProperty that I could set in the OnChildInitilized method of the replicator. My CreateTask MethodInvoking method the reads the information from the sequence property to populate it’s fields.

Custom SequenceActivity Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.ComponentModel;

namespace SemCrude_MOC_Workflow
{
    public partial class ReplicatorSequence : SequenceActivity
    {
        public ReplicatorSequence()
        {
            InitializeProperties();
        }

        public static DependencyProperty ChildDataProperty = System.Workflow.ComponentModel.DependencyProperty.Register("ChildData", typeof(Object), typeof(ReplicatorSequence));

        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Object ChildData
        {
            get
            {
                return ((Object)(base.GetValue(ReplicatorSequence.ChildDataProperty)));
            }
            set
            {
                base.SetValue(ReplicatorSequence.ChildDataProperty, value);
            }
        }

    }

}

When you create a custom activity using the above code, you will see a new activity in your toolbar that you can drag into your workflow just like any of the built in activities.  When you click on your activity and look in the properties tab of Visual Studio, you will see your custom property.  I did not assign a value for this property using the properties window but instead would set it programmatically as follows.

Replicator OnChildInitialized Method

private void PreModMgrApprovalReplicator_ChildInitialized(object sender, ReplicatorChildEventArgs e)
{
    ((ReplicatorSequence)e.Activity).ChildData = e.InstanceData;
}

In parallel mode, the replicator current index is always the last item in the InitialChildData Property. As a result you have to use the InstanceData from the ReplicatorChildEventArgs to get the current item being processed. This method sets the ChildData property of the Sequence, which is the activity of the replicator, to the current data. This method is executed for each item in the InitialChildData collection simultaneously, creating a new instance of the sequence for each item.

CreateTask MethodInvoking Method

private void CreatePreModMgrApprovalTask_MethodInvoking(object sender, EventArgs e)
{
    TaskItem item = (TaskItem)((ReplicatorSequence)((Activity)sender).Parent).ChildData;
    CreatePreModMgrApprovalTask.TaskId = Guid.NewGuid();
    CreatePreModMgrApprovalTask.TaskProperties.Title = item.TaskTitle;
    CreatePreModMgrApprovalTask.TaskProperties.AssignedTo = item.TaskUser;
    CreatePreModMgrApprovalTask.TaskProperties.SendEmailNotification = true;
}

As you can see, I have a custom class called TaskItem that I am using to pass the relevant information to the sequence to create a task. My CreateTask MethodInvoking method retrieves that value from my CustomSequence ChildData property and assigns the appropriate fields on my CreateTask activity.

Using this method, the appropriate number of unique tasks were created and when they were all completed the replicator activity is completed and the workflow moves on.

I hope this post saves someone the frustration I was experience attempting to get the parallel replicator working.

11 comments:

  1. Can you post a code or send it to my email Id, we are trying to do something similar. We are new to both workflow foundation and Sharepoint

    ReplyDelete
  2. maulikCE,

    What specific code are you looking for? I have posted everything necessary to create parallel tasks.

    ReplyDelete
  3. Thanks for the code. I have a doubt.I have created custom activity. Can you tell what I have to do in TaskItem class. Still my TaskChanged event is not firing.

    ReplyDelete
  4. rmanimaran ,

    My TaskItem class has no methods, it is just a collection of properties for me to store data. If your TaskChanged event is not firing the first thing I would suggest is to make sure that the TaskId property of the TaskChanged event is pointing to the TaskId property of the CreateTask event. You also need to make sure the TaskChanged event is using the same CorrelationToken as the CreateTask event and ensure that the BeforeProperties and AfterProperties and instantiated for the TaskChanged event.

    Hope that helps

    ReplyDelete
  5. Michael,
    In the custom activity, whether we need to place all other activities? I mean, in your code whether you have placed CreatePreModeMgrApprovalTask,while activity ,onTask changed and Complete Task activity in the custom Sequence activity and place the custom activity in main workflow?

    ReplyDelete
  6. rmanimaran,

    That is correct. All of the activities that one task has to go through are placed inside the custom sequence. The custom sequence is then placed inside a standard workflow replicator and in my case the replicator was set to run in parallel. As a result several instances of the custom sequence were fired at the same time. When all of the custom sequences are complete, the replicator completes and the workflow progresses. The replicator you see in the first screen shot I provided is indeed in the main workflow. In my case there are several other replicators throughout the workflow but they all follow the same logic as the one that I displayed.

    ReplyDelete
  7. I created the custom activity and compiled the solution. I am able to view it on my toolbox.

    when i try to add it to my workflow, it does not display the activities inside the custom activity.

    please suggest.

    thanks in advance.

    ReplyDelete
  8. sanjana,

    Do you mean you are not able to add the custom sequence to the workflow, or are you not able to add activities to the custom sequence?

    ReplyDelete
  9. Michael,

    I tried creating the custom activity again. I am able to add it to the workflow now. All the activities are running fine except when i add a sendmail to the custom activity.

    When i send a mail from custom activity, it is sending mails but the previous task is still in progress and new tasks are not visible on task list. there is no error while debugging.

    is there any way that i can send mail through custom activity.

    I require your help on this. Thanks.

    ReplyDelete
  10. sanjana,

    I'm not sure I understand. Does the sendmail activity come after the task is completed or after it is created? Is your replicator running in parallel or serial mode?

    I don't know of anything in the custom sequence that would prevent you from using the sendmail activity, however, I did not use the sendmail activity so I cannot say for sure.

    ReplyDelete
  11. Hi Michael,

    I am trying to send the mails after create task.

    please let me know how can i get the comments entered by reviewers from parallel custom activity to the task form?

    ReplyDelete