Workflow Execution Properties
Through thread local storage (TLS), the CLR maintains an execution context for each thread. This execution context governs well-known thread properties such as the thread identity, the ambient transaction, and the current permission set in addition to user-defined thread properties like named slots.
Unlike programs directly targeting the CLR, workflow programs are hierarchically scoped trees of activities that execute in a thread-agnostic environment. This implies that the standard TLS mechanisms cannot directly be used to determine what context is in scope for a given work item. For example, two parallel branches of execution might use different transactions, yet the scheduler might interleave their execution on the same CLR thread.
Workflow execution properties provide a mechanism to add context specific properties to an activity’s environment. This allows an activity to declare which properties are in scope for its sub-tree and also provides hooks for setting up and tearing down TLS to properly interoperate with CLR objects.
Creating and Using Workflow Execution Properties
Workflow execution properties usually implement the IExecutionProperty interface, though properties focused on messaging may implement ISendMessageCallback and IReceiveMessageCallback instead. To create a workflow execution property, create a class that implements the IExecutionProperty interface and implement the members SetupWorkflowThread and CleanupWorkflowThread. These members provide the execution property with an opportunity to properly set up and tear down the thread local storage during each pulse of work of the activity that contains the property, including any child activities. In this example, a ConsoleColorProperty
is created that sets the Console.ForegroundColor
.
class ConsoleColorProperty : IExecutionProperty
{
public const string Name = "ConsoleColorProperty";
ConsoleColor original;
ConsoleColor color;
public ConsoleColorProperty(ConsoleColor color)
{
this.color = color;
}
void IExecutionProperty.SetupWorkflowThread()
{
original = Console.ForegroundColor;
Console.ForegroundColor = color;
}
void IExecutionProperty.CleanupWorkflowThread()
{
Console.ForegroundColor = original;
}
}
Activity authors can use this property by registering it in the activity’s execute override. In this example, a ConsoleColorScope
activity is defined that registers the ConsoleColorProperty
by adding it to the Properties collection of the current NativeActivityContext.
public sealed class ConsoleColorScope : NativeActivity
{
public ConsoleColorScope()
: base()
{
}
public ConsoleColor Color { get; set; }
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
context.Properties.Add(ConsoleColorProperty.Name, new ConsoleColorProperty(this.Color));
if (this.Body != null)
{
context.ScheduleActivity(this.Body);
}
}
}
When the activity’s body starts a pulse of work, the SetupWorkflowThread method of the property is called, and when the pulse of work is complete, the CleanupWorkflowThread is called. In this example, a workflow is created that uses a Parallel activity with three branches. The first two branches use the ConsoleColorScope
activity and the third branch does not. All three branches contain two WriteLine activities and a Delay activity. When the Parallel activity executes, the activities that are contained in the branches execute in an interleaved manner, but as each child activity executes the correct console color is applied by the ConsoleColorProperty
.
Activity wf = new Parallel
{
Branches =
{
new ConsoleColorScope
{
Color = ConsoleColor.Blue,
Body = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Start blue text."
},
new Delay
{
Duration = TimeSpan.FromSeconds(1)
},
new WriteLine
{
Text = "End blue text."
}
}
}
},
new ConsoleColorScope
{
Color = ConsoleColor.Red,
Body = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Start red text."
},
new Delay
{
Duration = TimeSpan.FromSeconds(1)
},
new WriteLine
{
Text = "End red text."
}
}
}
},
new Sequence
{
Activities =
{
new WriteLine
{
Text = "Start default text."
},
new Delay
{
Duration = TimeSpan.FromSeconds(1)
},
new WriteLine
{
Text = "End default text."
}
}
}
}
};
WorkflowInvoker.Invoke(wf);
When the workflow is invoked, the following output is written to the console window.
Start blue text.
Start red text.
Start default text.
End blue text.
End red text.
End default text.
Note
Although it is not shown in the previous output, each line of text in the console window is displayed in the indicated color.
Workflow execution properties can be used by custom activity authors, and they also provide the mechanism for handle management for activities such as the CorrelationScope and TransactionScope activities.