Export (0) Print
Expand All

Creating Custom Tracking Services

Windows Workflow Foundation contains several services that you can plug into your hosting environment and start using immediately. In some instances, however, you may have to customize a particular service or create your own. Tracking services enable you to persist process information to a storage medium, including such things as a log file or a SQL Server database. This topic details the steps that are required to create your own customized tracking service.

Creating the Custom Tracking Service Class

Every component that receives tracking data must derive from the TrackingService base class. The host registers tracking services with the Windows Workflow Foundation runtime like any other Windows Workflow Foundation service. After a tracking service is registered, the tracking framework recognizes it and allows it to track workflow instance execution. For more information about how to register services, see How to: Add and Remove Workflow Services.

A custom tracking service is a class object that derives from the abstract TrackingService class. The TrackingService class contains several abstract methods that you must override to provide your custom tracking service behavior.

As an example, suppose that you want to create a custom tracking service that uses the system event log to output tracking information. To start, create a new class in your project and name it EventLogTrackingService. Derive the class from the TrackingService base class.

To generate the methods that are required to override the abstract base class methods, right-click the TrackingService class name, and select Implement abstract class 'TrackingService' in the shortcut menu. This generates the method definitions that you use to implement your custom tracking service.

Your code should appear similar to the following code example.

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Tracking;

namespace CustomTrackingService
{
    public class EventLogTrackingService : TrackingService
    {
        protected override TrackingProfile GetProfile(Guid workflowInstanceId)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        protected override TrackingProfile GetProfile(Type workflowType, Version profileVersionId)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        protected override TrackingChannel GetTrackingChannel(TrackingParameters parameters)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        protected override bool TryGetProfile(Type workflowType, out TrackingProfile profile)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        protected override bool TryReloadProfile(Type workflowType, Guid workflowInstanceId, out TrackingProfile profile)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
}

Tracking Profiles

The abstract methods that you must override from the TrackingService base class include working with TrackingProfiles and TrackingChannels. For more information about how to use TrackingProfiles, see Creating and Using Tracking Profiles. For more information about the TrackingService class, see TrackingService.

Before a channel is opened for the transmission of tracking data to your tracking service, you must provide a profile. This profile describes the type of tracking data that your tracking service wants to receive from the runtime engine. To do this, create a new TrackingProfile object and associate the profile with the ActivityTrackPoint and ActivityTrackingLocation objects. The workflow runtime engine will associate workflow types with tracking profiles by calling the TryGetProfile method of your object as shown in the following example. In this example, a single static method named GetDefaultProfile is used to create a default tracking profile.

ms735912.note(en-us,VS.90).gifNote:
When you call the GetProfile method, the version number that you pass into the method must correspond to the version number of the tracking profile that you want you use. Also, the version number must match between the tracking profile and the UpdateTrackingProfile stored procedure that is used with the SqlTrackingService service.

ms735912.note(en-us,VS.90).gifNote:
The example implementation for the GetTrackingChannel method is provided at the end of the next section.

// Add in a local variable and overloaded constructor methods.
protected WorkflowRuntime runtimeContainer = null;
public EventLogTrackingService()
{
}

public EventLogTrackingService (WorkflowRuntime container)
{
    runtimeContainer = container;
}

// Implement the TrackingService abstract methods.
protected override TrackingProfile GetProfile(Guid workflowInstanceId)
{
    // Instance profiles not implemented.
    throw new NotImplementedException("The method or operation is not implemented.");
}

protected override TrackingProfile GetProfile(Type workflowType, Version profileVersionId)
{
    return GetDefaultProfile();
}

protected override bool TryGetProfile(Type workflowType, out TrackingProfile profile)
{
    // Depending on the workflowType, service can return different 
    // tracking profiles. In this sample, the same profile is returned 
    // for all running types.
    profile = GetDefaultProfile();
    return true;
}

protected override bool TryReloadProfile(Type workflowType, Guid workflowInstanceId, out TrackingProfile profile)
{
    // Reloading profiles not implemented.
    profile = null;
    return false;
}

// Create the profile.
private static TrackingProfile GetDefaultProfile()
{
    TrackingProfile profile = new TrackingProfile();
    profile.Version = new Version("3.0.0");

    // Add activity track points.
    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 instance track points.
    WorkflowTrackPoint wtp = new WorkflowTrackPoint();
    WorkflowTrackingLocation workflowLocation = new WorkflowTrackingLocation();
    wtp.MatchingLocation = workflowLocation;

    foreach (TrackingWorkflowEvent workflowEvent in Enum.GetValues(typeof(TrackingWorkflowEvent)))
    {
        wtp.MatchingLocation.Events.Add(workflowEvent);
    }
    profile.WorkflowTrackPoints.Add(wtp);

    return profile;
}

Implementing the TrackingChannel

The TrackingChannel abstract class serves as a conduit that receives tracking events and data for a single workflow instance. The purpose of TrackingChannel is to provide a mechanism for tracking service writers to receive tracking information without having to consider thread safety. Because each workflow instance has only one thread of execution, there is never more than one thread active in a TrackingChannel object at a time. This reduces the need for data synchronization.

The TrackingChannel is the object that is used for the communication between the workflow and your tracking service. To create a TrackingChannel for the EventLogTrackingService, create an EventLogTrackingChannel class, derive the class from the TrackingChannel base class and implement the required abstract methods as you did for the TrackingService abstract class in the previous section.

When a workflow sends tracking information, it does so through the Send method that is defined in your TrackingChannel class. A TrackingRecord is passed as a parameter from which you can extract the tracking information. The following code example demonstrates how to extract data from the ActivityRecord parameter and output that information to the event log.

public class EventLogTrackingChannel : TrackingChannel
{
    // Add in a local variable and constructor method.
    private TrackingParameters parameters = null;

    public EventLogTrackingChannel(TrackingParameters parameters)
    {
        this.parameters = parameters;
    }

    // InstanceCompletedOrTerminated is called by Tracking runtime to 
    // indicate that the Workflow instance finished running.
    protected override void InstanceCompletedOrTerminated()
    {
        System.Diagnostics.EventLog.WriteEntry("EventLogTrackingService", "Workflow Instance Completed or Terminated");
    }

    // Implement the TrackingChannel abstract methods.
    protected override void Send(TrackingRecord record)
    {
        if (record is ActivityTrackingRecord)
        {
            ActivityTrackingRecord act = (ActivityTrackingRecord)record;

            System.Diagnostics.EventLog.WriteEntry("EventLogTrackingService", "Activity: " + act.QualifiedName + " - " +  act.ExecutionStatus );
        }
        else if (record is WorkflowTrackingRecord)
        {
            if (TrackingWorkflowEvent.Changed == ((WorkflowTrackingRecord)record).TrackingWorkflowEvent)
            {
                System.Diagnostics.EventLog.WriteEntry("EventLogTrackingService", "Workflow changes have been applied");
            }
        }
    }
}

The workflow runtime engine obtains a reference for your EventLogTrackingChannel by calling the GetTrackingChannel method defined in your EventLogTrackingService class. Create a new EventLogTrackingChannel object and return that object from the GetTrackingChannel method as shown in the following code example.

protected override TrackingChannel GetTrackingChannel(TrackingParameters parameters)
{
    return new EventLogTrackingChannel(parameters);
}

See Also

Community Additions

ADD
Show:
© 2014 Microsoft