Let’s look at how the app keeps track of what’s going on inside the runtime and the individual workflow instances.

 

All this functionality is factored into the WorkflowAdminService project, AdministrationService class.

 

This class has two sources of events – runtime itself (the WorkflowStarted, WorkflowSuspended, WorkflowCompleted, and WorkflowTerminated events) and a custom tracking service (TrackingEventsHelper class).

 

The tracking service creates a tracking profile for each workflow type (private TrackingProfile GetDefaultProfile(Type scheduleType) function) in which it essentially asks for all activity execution status events on all activities and all workflow events:

 

      TrackingProfile profile = new TrackingProfile();

      profile.Version = new Version("1.0.0");

 

      //all activities, all state changes

      ActivityTrackPoint atp = new ActivityTrackPoint();

      ActivityTrackingLocation location = new ActivityTrackingLocation(typeof(Activity));

      location.MatchDerivedTypes = true;

      foreach (ActivityExecutionStatus s in Enum.GetValues(typeof(ActivityExecutionStatus)))

            location.ExecutionStatusEvents.Add(s);

      atp.MatchingLocations.Add(location);

      profile.ActivityTrackPoints.Add(atp);

 

      // Add a TrackPoint to receive all workflow status events

      WorkflowTrackPoint workflowTrackPoint = new WorkflowTrackPoint();

      workflowTrackPoint.MatchingLocation = new WorkflowTrackingLocation();

      foreach (TrackingWorkflowEvent workflowEvent in Enum.GetValues(typeof(TrackingWorkflowEvent)))        workflowTrackPoint.MatchingLocation.Events.Add(workflowEvent);

      profile.WorkflowTrackPoints.Add(workflowTrackPoint);

 

In addition, if the workflow type has a property called Title, the tracking profile would have a request to extract a value of that property when the workflow enters the executing state (to make sure protected override void Initialize(IServiceProvider) has been called already):

 

      ActivityTrackPoint atp = new ActivityTrackPoint();

      ActivityTrackingLocation location = new ActivityTrackingLocation(scheduleType, new ActivityExecutionStatus[] { ActivityExecutionStatus.Executing});

      atp.MatchingLocations.Add(location);

     

      ActivityTrackingCondition matchingActivityTrackingCondition = new ActivityTrackingCondition("Name", rootActivity.Name);

      matchingActivityTrackingCondition.Operator = ComparisonOperator.Equals;

      location.Conditions.Add(matchingActivityTrackingCondition);

 

      WorkflowDataTrackingExtract workflowDataTrackingExtract = new WorkflowDataTrackingExtract();

      workflowDataTrackingExtract.Member = "Title";

      atp.Extracts.Add(workflowDataTrackingExtract);

 

      profile.ActivityTrackPoints.Add(atp);

 

 

The Initialize() method on the workflow class would set the title property to some instance specific value (e.g. in a document approval scenario it could be the document name along with the deadline and approver names).

 

Here is a simple example on how the title property could be declared and set:

 

public sealed partial class Workflow1 : SequentialWorkflowActivity

{

      private string title = string.Empty;

      public string Title

      {

            get { return this.title; }

      }

 

      protected override void Initialize(IServiceProvider provider)

      {

            base.Initialize(provider);

            this.title = "Some instance specific value.";

      }

}

 

When the tracking service receives a tracking event, it analyzes the tracking record and if it’s an activity tracking record, extracts the value of the title property:

 

      ActivityTrackingRecord activityTrackingRecord = record as ActivityTrackingRecord;

      if (activityTrackingRecord != null)

      {

            if (activityTrackingRecord.Body != null && activityTrackingRecord.Body.Count > 0)

            {

                  TrackingDataItem trackedItem = activityTrackingRecord.Body[0];

                  if (trackedItem.FieldName.Equals("Title", StringComparison.Ordinal))

                  {

                        string title = trackedItem.Data as string;

                  }

            }

      }

 

This title is then shown in the UI (in the instance list and on the details view) so that the user could distinguish between different instances of the same workflow type.