Using a TransactionScopeActivity within a ReceiveActivity in a state machine workflow

Part 1
part 2
Part 3
Part 4

In two previous blog posts, part one and part two, I covered the fact that the ReceiveActivity and the TransactionScopeActivity don’t work together very well. I demonstrated this using a sequential workflow and did show a, clumsy and hacky, workaround to get at least the initial request working. So while the request that starts the sequential workflow could work the same is not true for any subsequent calls.

But what about a state workflow?

State workflow’s are very different then sequential workflow’s because they are completely event driven. With every state you need to add an EventDrivenActivity and the first child should implement the IEventActivity interface. Well the ReceiveActivity does just that so we can use it to initiate a state machine workflow and triggers its events. However as you might expect a TransactionScopeActivity is not an IEventActivity so it cannot be the first child of the EventDrivenActivity. And that means you cannot wrap the ReceiveActivity inside of an TransactionScopeActivity, something that sort of solved the problem with a sequential workflow. Of course nesting the TransactionScopeActivity inside of the ReceiveActivity won’t work either for the reasons described in the first two post. So far so bad [:(]

So we need another workaround

Again there is no real good workaround [:(]. In fact the only one I can come up with is stop using the TransactionScopeActivity all together. So how to do transactions without using the TransactionScopeActivity?

Well the solution, ahem workaround, is to use the WorkflowEnvironment.WorkBatch object and add all pending work to it. The first parameter is an object of type IPendingWork and this is the one that is actually going to be called to do the work. The second parameter, the workItem, contains the data. Now when a workflow per persistence point is reached the IPendingWork object is called to do its work.

[Serializable()]
class Workflow1PendingWork : IPendingWork
{
    #region IPendingWork Members

    public void Commit(Transaction transaction, ICollection items)
    {
        foreach (var Workflow1PendingWork in items)
        {
            using (TransactionScope tx = new TransactionScope(transaction))
            {
                // Do my own transaction work
                tx.Complete();
            }
        }
    }

    public void Complete(bool succeeded, ICollection items)
    {
    }

    public bool MustCommit(ICollection items)
    {
        /// We want to commit!
        return true;
    }

    #endregion
}


The MustCommit function is called to check if the Commit needs to be called. The Commit itself is called as part of the workflow persistence point. Now you can use something like a CodeActivity and add your workItem to the batch like this:

WorkflowEnvironment.WorkBatch.Add(_pendingWork, myTransactionData);

 


When do we reach a persistence point?


This leaves us with the question of when these persistence point occur. The best way to think if these is when a workflow reaches a state that is final, no matter what happens. This includes the point where a workflow is done and when it is persisted to the persistence store. Using the SqlWorkflowPersistenceService with the PersistOnIdle option set to true will mean this will happen as soon as the event handler is done executing and the workflow is idle. However sometimes that just isn’t good enough and you need to do so sooner. Well the easiest way is to create a custom activity, I call it the PersistStateActivity, and decorate it with the PersistOnClose attribute. No need to add any code, all we are interested in is the attribute.

[PersistOnClose()]
public partial class PersistStateActivity : Activity
{
    public PersistStateActivity()
    {
    }
}

Now drop the activity at any point where you want your transactional data to be committed and the workflow runtime will take care of things for you [:)]


 


Conclusion


Although these workarounds are possible they are all but intuitive and it took quite a bit of digging around and brainstorming with some people to get this to work. So be very careful when using the TransactionScopeActivity, or any form of transactions for that matter, and the WCF ReceiveActivity!


Enjoy!

One thought on “Using a TransactionScopeActivity within a ReceiveActivity in a state machine workflow

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>