Replicator Tips and Tricks


Here are a few tips and tricks for using the ReplicatorActivity successfully.  It is a powerful activity which, when approached from the correct point of view, can be relatively easy to use.


Definitions


seed value - a value added to either of the ReplicatorActivity's child collections.  This value is associated with a single instance of the replicator's child.


template activity - the direct child of the replicator is referred to as the template activity.  At replicator startup one instance of the template activity will be run for each seed value in the child data collection.


Tips and Tricks


Use your activity properties!  Oftentimes users are confused as to where best to store the seed value.  It is with great dismay that the question, "Do I have to create a custom activity just to store the value?" is asked.  The answer is no, you don't have to create a custom activity just for value storage in some of the most common cases.


First, if your replicator is executing in Sequence and not Parallel then you can store the variable wherever you like, even outside the scope of the replicator.  Despite the fact that the object containing the variable will not be cloned once per instance since you are executing in sequence, you will always only have one copy of the template activity active at any given time.


Second, consider what you are doing with the value?  Chances are that the value you are using has some significance to one or more of the activities inside the template activity.  If this is the case, then store the value directly on that activity's property.  If more than one activity needs to access the data, then store it on one of them and use an ActivityBind to reference it on the rest of them.


Rarely you will find that the seed value is only used within the handler for a CodeActivity or that it must undergo some serious computation before it is settable to any of the activity properties.  If this is the case, then you will need to create a custom activity which will act as your template activity.  This should, however, be the rare case - perhaps instead your activities should be written to accept the seed value instead of some transformed version!


When is the UntilCondition evaluated?  The UntilCondition is evaluated in two places; it is evaluated once before any activities are executed and once after each instance of the template activity closes.  Note that if it returns true when the replicator first starts executing then the replicator will close immediately and will not execute any children.  Also, any children still executing when the UntilCondition evaluates to true will be cancelled.


How can I tell which child just closed when the UntilCondition is evaluated?  Unfortunately, there is no built in mechanism for this; the UntilCondition does not accept parameters and there is no global nor ReplicatorActivity defined accessor for this kind of data.  There is, however, a suitable workaround for determining the newly completed activity. 


At a child's completion the replicator will first raise the ChildCompleted event and then evaluate the UntilCondition.  Since these two actions occur in the same method and Windows Workflow Foundation guarantees a single thread per workflow instance (see post on Parallelism) you can be sure that no other chlidren will execute between the event handler running and the condition evaluation.


That said, some identifying data from the completed child can be stored in a well known location (variable on the root activity, property of some other activity, etc) during the ChildCompleted handler and read from the location by the UntilCondition.


Comments (7)

  1. slavik says:

    Thanks for the tips on the replicator.

    I was trying to implement some auction procedure using it and now wonder if my scenario is correct. You can see the workflow at:

    http://static.flickr.com/121/290421062_d9a2d5d66c.jpg?v=0

    The first client schedules the auction and then the others are sending their bids, and I’m using webServiceInput-Output. The problem is that after the first client will be answered by the web service, the replicator does not wait for the inputs from clients. I completes its children (fires the events) and completes in the end.

    In the case if I move the replicator on first place in the workflow it waits for only the first replicated webServiceInput-Output and then instantly completes all the other children without receiving input from clients.

    I’m using cookies in this scenario to root the requests to the workflow, but despite the rigth cookie names and values the published workflow doesn’t work as expected. Is the solution incorrect?

  2. ntalbert says:

    slavik, the long story short is that you cannot use WebServiceInput/WebServiceOutput inside a Replicator.  The reason is that these activities do not support correlation in any manner – in  your scenario there is no way to correctly route one client’s request to a specific WSInput.  For more information on this see correlation.

    If you want to enable the scenario you mention you will be better off using HandleExternalEventActivity and CallExternalMethodActivity and writing your own web service host.  While the latter part of this sounds like a major undertaking, writing your own simple web service based host is actually not that bad.  Consider the following:

    public class SomeService : WebService

    {

     private WorkflowRuntime runtime;

     public WorkflowRuntime Runtime

     {

       get

       {

         // You can either specify a config section or do programmatic configuration here

         if (runtime == null) runtime = new WorkflowRuntime();  

         return runtime;

       }

     }

     [WebMethod]

     public BidResult EnterBid(Guid instanceId, string clientId, decimal bidAmount)

     {

       BidService bidServ = Runtime.GetService<BidService>();

       return bidServ.EnterBid(Guid instanceId, clientId, bidAmount);

     }

    }

    Note that the code above is somewhat simplified (config of runtime is left out, BidService implementation is left out, actually getting to BidService will require accessing ExternalDataExchangeService) but this is the psuedocode that will get you to where you want to be.

    BidService is meant to be an ExternalDataExchange service.  EnterBid, for example, would raise an event into the workflow and then block waiting for the corresponding response to come back out.  You can model this as such:

    // EnterBid style code

    dictionaryOfWaitHandles.Add(responseIdentifier, new ManualResetEvent(false));

    someEvent(…) // one of the parameters is the "response identifier"

    dictionaryOfWaitHandles[responseIdentifier].WaitOne();

    dictionaryOfWaitHandles.Remove(responseIdentifier);

    object result = dictionaryOfResponses[responseIdentifier];

    dictionaryOfResponses.Remove(responseIdentifier);

    return result;

    // CallExternalMethodActivity targetted method which handles the "response"

    dictionaryOfResponses.Add(responseIdentifier, response);

    dictionaryOfWaitHandles[responseIdentifier].Set();

    Again, this is a very simple approach – it assumes a single output, assumes you can block the thread, assumes you aren’t using the ManualScheduler, etc.  But, this is the basic idea on which you can build what you are looking for.

  3. slavik says:

    Thanks. I supposed that the message is probably "consumed by all the replicated instances at the same time" which means there’s no correlation support. But thought there might be some workaround or I’m making some mistake in the scenario. It’s bad that the documentation never says that about the webServiceInput.

  4. Due to popular demand, here are some answers to the questions. Well, not answers exactly … just…

  5. Due to popular demand, here are some answers to the questions . Well, not answers exactly … just some

Skip to main content