Using data driven workflow activities and repeated correlation

How's that for a title?  The driver for this post is that I'm training a lot of folks right now on using WF to build workflows, and when we cover correlation, the example we use has two branches in a parallel activity where we correlate within each branch.  The question inevitably comes up about how to do this dynamically, where the number of "branches" is driven by data.  So, I whipped up this example that uses code from the Microsoft Windows Workflow Foundation Labs (Beta 2) to showcase the Replicator Activity and the Conditioned Activity Group (CAG).  These two activities provide for richer models of business rule or data driven replication using correlation. 
 
In order to enable my example, I have created a composite activity that wraps up the voting behavior found in the communications lab.  This gives me a reusable component that I can then use in multiple places in my workflow, and it also gives me the ability to set properties on the set of activities (now my single composite activity) for each instance.  For example, one of the key properties I can set is the alias, or name, of the user who should vote.  In addition, I can define events at the composite activity that my workflow can listen for and handle.  This allows my workflow to take action when a voter approves or rejects a ballot. Finally, I define a correlation token within the activity and set the parent activity to the composite activity itself.  This provides the context in which my correlation token is valid and is the key to allowing me to correlate within each activity.  Note that it is not required that you do this in a composite activity, the parent activity for your correlation token can be a sequence or some other container.  Finally, I added a property to my workflow called Aliases of type List<string>  which holds the data for my workflow.  I pass in this data from program.cs via parameters to the workflow. 
 
In my workflow, I have two main activities, the first is a replicator which shows how to use this activity to do data driven correlated activities, and the second is the Conditioned Activity Group which allows you to use rules/conditions to determine what branches of activities to run, and when I am done running them. 
 
The replicator activity in my example has the InitialData property bound to the workflow property for aliases, so I am going to run whatever activities are in the replicator as many times as I have items of data.  I also need to pass that data to each instance of the voting activity before it is executed (specifically, the alias it should use).  I use the Child_init event on the replicator to set the alias based on the current data item.  The current data item lets me get the current item in my collection of data and I can then use that to configure the currently executing activity or set of activities.  I also set the execution type property on my replicator to indicate if I want all the branches to run in parallel, or in sequence.  Very powerful option!
 
For the CAG, I have included two voting activities, and bound their aliases to two different instances of input data on the workflow.  I've done this using indexed values from the List<string> property.  When dealing with a CAG, you have to set the "when" property on each branch of execution which indicates the rule or code to determine if the particular branch should execute.  On the CAG itself, you provide a "Until" property that likewise specifies code or rules, but these provide the condition under which execution should stop.  So, the CAG will continue running (foreach/while style looping) until the "Until" condition is met. On each interation, it will use the "when" condition on each activity branch to determine whether it should run that branch.  So you get replication, but with rules to guide it.  You get a lot more than that, but this is a simple example. 
 
When you run the program, it might help to disable one activity and focus on the output by the other.  Running the replicator will give you three vote dialogs, each with the appropriate users name, and the console will appropriately log their votes.  We correlate the response back into the workflow on each instance of the votingactivity.  When you run the CAG, you'll see that the first time through, both voting activities will run.  Until you vote "yes" on one of the items, the CAG will continue to run and use the "when" condition to determine which activity branches to run.  Play around with your voting and notice what happens if you continue to vote no with one person for a few rounds, then vote yes.
 
The final thing to notice is that we have a need to catch the situation, with our CAG, where someone voted yes, but the other person has not yet responded.  If we don't deal with this scenario, when the second person votes, they will get an exception.  We use the WorkflowQueueInfo data from the workflow instance to query the workflow and see if it is waiting for any responses.  If it is not, then we gracefully exist instead of trying to raise the event.  If it is, then we go ahead and raise the event.  This is one way you can attempt to avoid the EventDeliveryFailedException. 
 
Hopefully these examples will prove useful for someone.  I plan to do my best at creating more examples to share.  As always, feedback is welcomed.  You can get the code here
 

Posted Feb 21 2006, 12:11 PM by matt-milner

Comments

Jason Dossett wrote re: Using data driven workflow activities and repeated correlation
on 02-22-2006 5:22 AM
Excellent!
SuryaPrakasarao wrote re: Using data driven workflow activities and repeated correlation
on 03-24-2006 1:54 AM
Hi,

I need some explination about working procedure of state Machine
Tan Ke wrote re: Using data driven workflow activities and repeated correlation
on 01-08-2007 7:29 AM
if i call CreateBallot("Jim") twice,it will get a EventDeliveryFailedException in VotingService,how can do?
thanks
Matt Milner wrote re: Using data driven workflow activities and repeated correlation
on 01-10-2007 6:14 AM
If you have already received the event from Jim and the workflow has moved on to another step, then you will get this exception when you try to submit the "vote"/message again. In order to be able to submit more than once, you need a looping mechanism. In the sample, the CAG activity (disabled by default I think) does just this by continuing to prompt for votes until some condition is met.
However, you can still run into problems with this, so you either need to check the workflow to see if it is expecting this message, or catch the exception.
You can check the workflow using the GetWorkflowQueueData method on the WorkflowInstance class.
Ke Tan wrote re: Using data driven workflow activities and repeated correlation
on 01-11-2007 1:19 AM
Thank you Matt.sorry for my poor english.
i think i got your means. in CAG activity,there are two VotingActivity type: voter1 and voter2,but in ReplicatorActivity,there are only one VotingActivity type:votingActivity1,if ReplicatorActivity.ExecutionType = Parallel,three votingActivity1 instance will be created and interactively running.in this situation,if IVotingService.CreateBallot("Jim") called twice,a EventDeliveryFailedException will be throw when event fired second time.my question is:

try
{
if (result == DialogResult.Yes)
{
if (ApproveProposal != null)
ApproveProposal(null, votingEventArgs);
}
else
{
if (RejectProposal != null)
RejectProposal(null, votingEventArgs);
}
}
catch (EventDeliveryFailedException ex)
{
Console.WriteLine("One votingActivity1 instance is still waiting for the event,how can i forward this exception to that instance?");
}
Matt Milner wrote re: Using data driven workflow activities and repeated correlation
on 01-11-2007 8:44 AM
If you are trying to send the "vote" to the remaining activity, then you need to use the correct correlation token (in this case the person's name is the correlation token) or the message won't get routed correctly. So, if you caught the exception for "Jim" you could change the name property and submit it for one of the remaining activities. This supposes however that a) the correlation token is not all that important, which is probably is and/or b) you know what the remaining correlation token is that you need to use, which you probably don't.

If you don't need correlation, then you can run this without it and submit X number of votes.

If, however, you want to handle the exception in the workflow, I think you are out of luck. Since the workflow will not get the message, you have to handle it in your host code. Otherwise, you could model an event handler (using eventhandlingscope activity) and then raise a generic event that indicates an error in the host so you can handle it in the workflow.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?