Search code examples
workflow-foundation-4

Windows Workflow 4 - when are tracking records emitted?


I have a long running activity (where the activity is continually doing work for a long period of time - not an activity that is waiting for a response from an external source) and I want to report progress from that activity. However, I cannot get this to work.

I have a simple console app that runs the activity:

class Program
{
    static void Main(string[] args)
    {
        var wfApp = new WorkflowApplication(new ActivityLibrary.ProgressActivity());

        var autoResetEvent = new AutoResetEvent(false);
        wfApp.Completed = e => autoResetEvent.Set();
        wfApp.Extensions.Add(new ConsoleTrackingParticipant());
        wfApp.Run();
        autoResetEvent.WaitOne();

        Console.WriteLine("Done");
        Console.ReadLine();
    }
}

internal class ConsoleTrackingParticipant : TrackingParticipant
{
    protected override void Track(TrackingRecord record, TimeSpan timeout)
    {
        Console.WriteLine(record.EventTime.ToString());
    }
}

I've tried implementing the ProgressActivity in two ways. First I tried deriving from CodeActivity, but when I use this implementation I receive all the custom tracking records together once the workflow has completed (though the reported record.EventTime is correct):

public sealed class ProgressActivity : CodeActivity
{
    protected override void Execute(CodeActivityContext context)
    {
        for (var i = 0; i <= 10; i++)
        {
            var customTrackingRecord = new CustomTrackingRecord("ProgressTrackingRecord")
                {
                    Data = { { "Progress", i * 10.0 }}
                };

            context.Track(customTrackingRecord);
            Thread.Sleep(1000);
        }
    }
}

I then tried deriving from AsyncCodeActivity, but in this case I get an "ObjectDisposedException: An ActivityContext can only be accessed within the scope of the function it was passed into" on the context.Track line:

public sealed class ProgressActivity : AsyncCodeActivity
{
    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        Action action = () =>
            {
                for (var i = 0; i <= 10; i++)
                {
                    var customTrackingRecord = new CustomTrackingRecord("ProgressTrackingRecord")
                    {
                        Data = { { "Progress", i * 10.0 } }
                    };

                    context.Track(customTrackingRecord);
                    Thread.Sleep(1000);
                }
            };

        context.UserState = action;
        return action.BeginInvoke(callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        ((Action)context.UserState).EndInvoke(result);
    }
}

Can anyone explain where I've gone wrong?

P.S. I realise that the approach I've shown would not scale well if the workflow was ever to run on a server, but the application I'm building is a desktop application.

[EDIT] I guess I'm actually asking a broader question here: when are tracking records emitted by the workflow execution engine? My investigations suggest that all the records for a specific activity are emitted after the activity has completed. Is there any way to force them to be emitted during the execution of the activity?


Solution

  • I've also asked this question on the MSDN workflow forum, and done some further investigation myself based on the comments I received there. As a result I think that tracking records are emitted when the activity completes or is bookmarked. See http://social.msdn.microsoft.com/Forums/en-US/wfprerelease/thread/8ce5dee9-9a19-4445-ad7f-33e181ec228b for more info.