Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Workflow Foundation and writing Activities that run for a while
Mike Taulty's Blog

Mike's Badges

Follow on Twitter
View mike's profile on slideshare
Add to Technorati Favorites
CW Blog Awards
Writing a Windows Workflow Activity that performs some kind of quick, synchronous operation seems relatively easy.

I’ve managed to get away with using my MessageBoxActivity for parts of demos and there’s really not much to it;
 
public partial class MsgBoxActivity : Activity
{
public MsgBoxActivity()
{
InitializeComponent();
}
public static DependencyProperty TextToShowProperty =
DependencyProperty.Register("TextToShow",
typeof(string), typeof(MsgBoxActivity));
 
public string TextToShow
{
get { return ((string)base.GetValue(TextToShowProperty)); }
set { base.SetValue(TextToShowProperty, value); }
}
 
protected override ActivityExecutionStatus Execute(ActivityExecutionContext   
  executionContext)
{
MessageBox.Show(TextToShow);
 
return (ActivityExecutionStatus.Closed);
}
}
 
 
Now, whilst that particular message box is on the screen the Runtime doesn’t have much chance to do anything else with this particular Workflow Instance.
 
The Runtime has at most one thread wandering through a Workflow Instance at any particular time so if you block that thread (e.g. MessageBox.Show) then no other Activities within that particular Instance can be executed.
 
If the Activity was nested inside a Parallel Activity then it would block any Parallel work going on until the message box was dismissed. Similarly, the Runtime does not see the Workflow go “Idle” and won’t be able to persist it whilst that message box is on the screen.
 
Activities that block like this or perform long running work can be written to work better with the Runtime. Rather than the Activity blocking, it can pair itself up with a custom Runtime Service (derive from WorkflowRuntimeService) and perform an interaction such as;
 
  1. Activity executes.
  2. Activity requests its custom Runtime Service from the Runtime.
  3. Activity requests its custom Runtime Service to begin the long-running operation on its behalf.
  4. Activity returns state of “Executing” to Runtime.
  5. If (4) causes the Instance to go Idle then the Runtime can now persist the Instance.
  6. Custom Runtime Service completes long-running operation for that Activity.
  7. Custom Runtime Service requests that the runtime notify the Activity that the operation is done.
  8. Activity receives notification and closes itself.
 
Note that this association between an Activity and an associated custom Runtime Service that gets plugged into the Runtime means that the Activity in question becomes a bit useless without its companion custom Runtime Service but there’s no real way around that.
 
There’s also no way to check at compile time that the correct custom Runtime Service has actually been configured into the Runtime by the developer so the Activity would really have to handle that itself as an error condition.
 
Note also that if you’re not writing an Activity but, instead, you’re writing a Workflow that waits for something “outside” of that Workflow to occur then you can use the Local Communication Service to achieve that and it’s a deal simpler. There’s a great video on doing just that here J
 
Returning to the steps 1-8 above, the tricky bit is really step (7) where the Activity and the custom Runtime Service have some kind of communication once the long-running operation has completed.
 
The reason why it’s a bit tricky is because of persistence. In the time between when the Activity asks its partner Runtime Service to do some work on its behalf and when that work actually completes there’s the possibility that a number of things might happen;
 
  • The Workflow containing the Activity might go Idle and then be persisted out to a store. This means that you can’t just fire an event from the Runtime Service back to the Activity because the Activity is likely to be gone. You need to use some kind of facility in the Workflow Runtime in order to make this work properly in the face of persistence.
  • The Runtime might be shut-down. If it stays shut down then there’s not much that you can do about that but if it comes back and brings the Runtime Service back then you’d expect that service to continue the work that it was doing on any Activity’s behalf before it was shut down.
 
In order to deal with (1) the Runtime provides a queuing facility that can communicate with an Instance regardless of whether the Instance is currently present in memory or not - this queuing service provides the underpinning of the higher level Local Communication Service. Web searches for WorkflowQueuingService and/or WorkflowQueue unfortunately don’t give much in the way of results at the moment so I can’t point to a good definition of how this stuff works beyond the MSDN docs.
 
So, if we want to build a new kind of MessageBoxActivity that works in this new way rather than the blocking mechanism we’ve got right now then we need to start again and factor things out a little.
 
Here’s my revised Execute override;
 
    protected override ActivityExecutionStatus Execute(
      ActivityExecutionContext executionContext)
    {
      WorkflowQueuingService queueService =
        executionContext.GetService<WorkflowQueuingService>();
 
      MessageBoxService msgboxService =
        executionContext.GetService<MessageBoxService>();
 
      RequestMessageBoxFromService(queueService,
        msgboxService);
 
      return (ActivityExecutionStatus.Executing);
    }
 
So, here we go ahead and ask for 2 services from the Workflow Runtime. The first is the inbuilt WorkflowQueuingService and the second is my custom service MessageBoxService which I now depend upon to get MessageBoxes displayed without stalling my Instance.
 
We then use both of these services to set up some activity in RequestMessageBoxFromService;
 
    private void RequestMessageBoxFromService(WorkflowQueuingService
      queueService, MessageBoxService msgboxService)
    {
      WorkflowQueue queue = queueService.CreateWorkflowQueue(
        instanceQueueName, false);
 
      queue.RegisterForQueueItemAvailable(this);
 
      WPFMessageBoxState state = new WPFMessageBoxState();
      state.WorkflowId = WorkflowInstanceId;
      state.InstanceQueue = instanceQueueName;
      state.MsgBoxText = TextToShow;
 
      msgboxService.AddMessageBox(state);
    }
 
Here, we’re doing a couple of things. We’re using the WorkflowQueuingService in order to create a queue with a specific GUID name and we also set ourselves up to receive an event when a message appears on that queue (telling us that the MessageBox has been closed). In order to do this it seems that we have to provide an implementation of IActivityEventListener<QueueEventArgs> and I implement that on my Activity so it can be notified on queue messages. I’ll return to that later on.
 
When the MessageBoxService gets a request for a MessageBox it needs a bunch of state in order to be able to get “back” to the Workflow Instance at a later point and so I pass across the Workflow Instance Id, the unique name of this particular queue and the text that the message box is to display (this helps with some persistence that I do later on).
The MessageBoxService then displays the message box on the screen and waits for the user to close it. When the user does close it, the MessageBoxService catches that event and uses it to notify the waiting Activity;
 
 
    private void OnMessageBoxClosed(object sender,
      MessageBoxClosedEventArgs eventArgs)
    {
      WorkflowInstance instance = Runtime.GetWorkflow(
        eventArgs.State.WorkflowId);
     
      instance.EnqueueItem(eventArgs.State.InstanceQueue,
        "Arbitrary data of object type", null, null);
    }
 
So, when someone closes one of these MessageBoxes we use the state that we’ve captured to remember which Workflow Instance that MessageBox belongs to, which unique Queue within there we need to send the message to (imagine multiple MessageBoxActivities running in one Instance at a time) and we send some data down that queue to the Activity to say “Hey, your messagebox is closed”.
 
Back in the Activity, this is picked up in;
 
    public void OnEvent(object sender, QueueEventArgs e)
    {
      ActivityExecutionContext context = sender as
        ActivityExecutionContext;
 
      WorkflowQueuingService queueService =
        context.GetService<WorkflowQueuingService>();
 
      WorkflowQueue queue =
        queueService.GetWorkflowQueue(instanceQueueName);
 
      // Arbitrary here, could be used for some
      // real async response value.
      string receivedData = queue.Dequeue() as string;
 
      queueService.DeleteWorkflowQueue(instanceQueueName);
 
      context.CloseActivity();
    }
 
Where we pick up the queue event (notice that we don’t do anything with the data itself which is just an arbitrary string but we could use it to communicate something more useful as an asynchronous return value from the long-running operation that the custom Runtime Service has run for us) but we instead just get rid of the queue that we created and close off the Activity.
 
We’re still left with dealing with item (2) from way back earlier in the post which is – what if someone shuts down the Runtime (and/or the host application) which would mean that our custom Runtime Service (the MessageBoxService) gets shutdown. What if it’s shut-down at the point where it’s actively waiting with N MessageBoxes on the screen.
 
As far as I can work out, there’s no “service persistence” mechanism built into the Runtime in the way that there is “Instance persistence” and “Activity persistence” built in. However, a custom Runtime Service that is derived from WorkflowRuntimeService does get Start/Stop notifications from the Runtime and so these can be hooked in order to add some code that persists/loads the state from the custom Runtime Service as and when the Runtime is brought in and out of existence so it’s just a matter of adding some kind of out-of-band solution to persist the state for each custom Runtime Service.
 
The rest of the code that I ended up writing relates to STA threads and playing around (probably incorrectly) with WPF in order to get a few message boxes onto the screen from a single thread.
 
As usual, this is just me having a quick play with bits and pieces so it’s possibly incorrect and open to being improved in all kinds of places. In particular, it doesn’t deal with anything like Cancellation of the Activity which you’d need to factor in.
 
There’s also a more complete sample in the SDK – have a look for the FileWatcher sample for a more fully featured implementation of something similar.
 
However, with all that said – here’s the VS solution (Beta 2 bits). Hopefully the bits are reasonably obvious in the solution (note that the persistence has a hard-coded path to c:\temp which is a bit naughty and also that the connection string to the SQL database for the SqlWorkflowPersistenceService is hard-coded too).
 

Posted Wed, Aug 2 2006 5:44 AM by mtaulty

Comments

Mike Taulty's Blog wrote Workflow - State Machines, Storing State
on Mon, Sep 11 2006 3:42 PM
This is in follow up to a mail I got the other day on a similar theme. Most Workflows are likely to have...
Mike Taulty's Blog wrote MSDN Article on Creating Custom WF Activities
on Wed, Nov 22 2006 4:32 PM
Nice article over on MSDN about creating activities for WF. I've done a few WF talks this week and I...
(C) Mike Taulty, 2009. All rights reserved. The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Inappropriate comments will be deleted at the authors discretion. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems