Welcome to Windows Workflow Foundation (WF)
Top Tasks :

WF Community Bloggers

Wednesday, May 14, 2008 - Posts

  • Using multiple WorkflowRuntime objects in a single AppDomain

    For some reason a lot of people seem to think that only a single WorkflowRuntime object can be created in an AppDomain. And it isn't just the average Joe out there who seems to think so, no even book authors describe this behavior as the Workflow Foundation book I am reading now does so.

    Just to set the record straight I will shout this out once more:

    You can have multiple WorkflowRuntime objects in a single .NET AppDomain

    Now I know where this perception comes from and its the original beta of Workflow Foundation way back before .NET 3.0 was released. Back then the WF team decided that only a single WorkflowRuntime would be enough for an AppDomain and created this restriction. I am not sure if they actually had a requirement or not but when people objected they listened and lifted the restriction.

    The removal of this restriction is a good thing as multiple WorkflowRuntime objects can be useful. The main reason is that different workflow's can have different, and conflicting, requirements of runtime services. And as a single WorkflowRuntime can have only a single runtime service configuration, and that should never be changed while the WorkflowRuntime is active. For example just think of one workflow that is long running and needs a WorkflowPersistenceService while a second is short running and should never be persisted.

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Working with the ReplicatorActivity and an UntilCondition

    The ReplicatorActivity is one of the more interesting Workflow Foundation activities to work with when you have a collection of data and want to iterate over each item. Basically just think of it as a For Each statement in regular code.

    To demonstrate the behavior I have created a very small workflow that looks like this in the designer:

    image

    Or in case you prefer the XOML like this:

    <SequentialWorkflowActivity x:Class="WorkflowConsoleApplication5.Workflow1"
                                x:Name="Workflow1"
                                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
      <ReplicatorActivity x:Name="replicatorActivity1"
                          ChildInitialized="SetupChildData"
                          Initialized="SetupData"
                          ExecutionType="Sequence">
        <ReplicatorActivity.UntilCondition>
          <CodeCondition Condition="RepeatUntill" />
        </ReplicatorActivity.UntilCondition>
        <CodeActivity x:Name="codeActivity1"
                      ExecuteCode="codeActivity1_ExecuteCode" />
      </ReplicatorActivity>
    </SequentialWorkflowActivity>

    The codebehind file I used looks like this:

    Public Class Workflow1
        Inherits SequentialWorkflowActivity
    
        Private Sub codeActivity1_ExecuteCode(ByVal sender As Object, _
                                              ByVal e As EventArgs)
            Dim current As CodeActivity = sender
            Dim data = GetCurrentData(current.Parent)
            Console.WriteLine("The data is '{0}'", data)
        End Sub
    
        Private Sub SetupData(ByVal sender As Object, _
                              ByVal e As EventArgs)
            Dim replicator As ReplicatorActivity = sender
    
            Dim data() As String = _
            {"One", "Two", "Stop here", "Three"}
    
            replicator.InitialChildData = data
        End Sub
    
        Private Sub RepeatUntill(ByVal sender As Object, _
                                 ByVal e As ConditionalEventArgs)
            Dim replicator As ReplicatorActivity = sender
            Dim data = GetCurrentData(sender)
            Console.WriteLine("Testing with data '{0}'", data)
            If data = "Stop here" Then
                e.Result = True
            ElseIf replicator.AllChildrenComplete Then
                e.Result = True
            End If
        End Sub
    
        Private Sub SetupChildData(ByVal sender As Object, _
                                   ByVal e As ReplicatorChildEventArgs)
            Console.WriteLine("Starting child with '{0}'", _
                              e.InstanceData)
        End Sub
    
        Private Function GetCurrentData(ByVal sender As Object)
            Dim replicator As ReplicatorActivity = sender
            Dim result = replicator.CurrentChildData(replicator.CurrentIndex)
            Return result
        End Function
    End Class
    

    Using the without an UntilCondition is actually quite simple. The main thing to be aware of the ExecutionType property. In this case I didn't set it and it defaults to Sequence. This means that each iteration comes after the next iteration and we can use the CurrentIndex and CurrentChildData to retrieve the piece of data we are working with. When we set the ExecutionType to Parallel the ReplicatorActivity behaves very different. In that case each branch is started at the same time so the CurrentIndex, which is scoped to the ReplicatorActivity, doesn't help you. In fact it will return the last item of the collection every time you try.

    In the case of parallel execution the way to get at the data is the ChildInitialized event. This event has a parameter of type ReplicatorChildEventArgs which in turn contains a property InstanceData which is the data this iteration is going to work with. its your responsibility to pass this InstanceData on to the activity inside the ReplicatorActivity. The usual thing to do here is create a custom, possibly sequence, activity with an extra property to hold the data and store it there in the ChildInitialized event.

     

    There are a few interesting, and unexpected, thing to be a aware of when you use an UntilCondition with a ReplicatorActivity. When I run this simple test workflow I receive the following output:

    image

    There are a couple of interesting things to note:

    1. The first time the UntilCondition is called is before the first ChildInitialized event and when we retrieve the current data it actually returns the last item. As far as I can tell there is no standard way to see this is case so if could be a problem you need to take care of it yourself. As far as I am concerned this is a serious shortcoming of the ReplicatorActivity
    2. The UntilCondition fires after and not before each loop is executed. Basically this means it is easy to use the execution result of an iteration and decide to stop further iterations but hard to prevent all remaining iteration, including the current, based on the input data. Again you would need to add some conditional logic inside the ReplicatorActivity. No problem and actually quite a nice behavior but something to be aware of.
    3. The ReplicatorActivity never stops if you don't tell it to. This is the behavior I most dislike with the ReplicatorActivity. Instead of just stopping and continuing with the next activity when all data is processed it just stops Sad. Now this isn't the case when you don't have an UntilCondition, in that case every item is processed and when done it proceeds with the next activity just as you would expect. No with an UntilCondition you are responsible for stopping and to do so the UntilCondition is actually called one more time after the last item has been processed. The thing to check for is the AllChildrenComplete property of the ReplicatorActivity. When this is true all the data has been processed and you are responsible for stopping the ReplicatorActivity. Now this might not be hard but it always is a major point of confusion and, I presume, catches everyone at least the first time. And worse I can see no benefit at all for this behavior as the ReplicatorActivity just stops and the next activity is never scheduled. As far as I am concerned this is a big "bug" in the behavior of the ReplicatorActivity.

    Removing the test for the "Stop here" in the UntilCondition result in the following output:

    image

    As you can see there is one additional UntilCondition at the end at which time the AllChildrenComplete property is set to true.

     

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

<May 2008>
SuMoTuWeThFrSa
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

Copyright © 2006 Microsoft Corporation. All Rights Reserved. | Terms of Use | Privacy Statement | Contact Us