(WF4) Lesser Known WF Features: WorkflowDataContext
Sometimes it happens that via the forums I learn about a new [to me] beast in the WF Zoo. Today that animal is WorkflowDataContext. The MSDN document is terse:
“Represents the data context of the current workflow environment and provides a bridge to bring workflow arguments and variables into the scope of data binding.”
Let's put that into everyday WF terms. How would you actually use one of these?
Application 1: Reading Variable and Argument Values from inside a C# custom Activity
It’s a simple workflow. Not shown in the picture above is an InArgument<string> called “argument1”. Also not shown above is that CodeActivity1 has an InArgument<string> called “Text” which is assigned expression “someText”.
CodeActivity1
Here’s a custom C# activity CodeActivity1 which will access the WorfklowDataContext.
public sealed class CodeActivity1 : CodeActivity { public InArgument<string> Text { get; set; }
protected override void Execute(CodeActivityContext context) { string s = Text.Get(context);
WorkflowDataContext dc = context.DataContext; foreach (var p in dc.GetProperties()) { Console.WriteLine("Property {0}: {1}", ((PropertyDescriptor)p).Name, ((PropertyDescriptor)p).GetValue(dc)); } } } |
Notes:
-The DataContext property is defined on ActivityContext base class , so it's also available for writing NativeActivity and AsyncCodeActivity.
-The required parameter to PropertyDescriptor.GetValue is the WorkflowDataContext object. It acts as an explicit ‘this’ object for doing a property get.
Output
Here’s what we see if we run a simple console app which invokes the workflow and passes in a value for argument1.
Notably absent from this list is ‘Property Text’, the InArgument<string> of CodeActivity1 itself.
Application 2: Setting Variable Values or Argument Values from inside a CodeActivity
PropertyDescriptor.SetValue() seems to work equally well as PropertyDescriptor.GetValue() does.
Limitations - What can’t you see in WorfklowDataContext?
We noticed above that we can’t actually get the value of the InArgument<string> ‘Text’ which is on this activity from our WorkflowDataContext . Is there anything else we can’t see from WorkflowDataContext? Well, yes. Let’s refactor the above workflow by introducing a composite Activity ‘Activity1’ which wraps the inner Sequence, Sequence2:
Workflow1.xaml
Activity1.xaml
Output
We can run the program again, and get a different result:
The conclusion?
Only variables (and arguments) in the parent activity’s public scope are visible in WorkflowDataContext.
For any CodeActivity you drop into a composite activity XAML (<Activity x:Class>), it is effectively limited to seeing only Variables and Arguments from that same XAML file. (This sounds like a good thing from a composability view.)
When is it useful?
Seeing WorkflowDataContext used as a solution to a problem, I immediately react “Wow, this class is really interesting…. especially if it works. But when is using it a good idea?”
The main thing I am thinking about while asking that question is the surprise factor. This feature isn’t dealing with anything ‘esoteric’ or ‘invisible’ in the workflow, such as context properties or extensions. In fact the opposite – it’s dealing directly with everyday Variables and Arguments. As a WF4 designer user I’m only accustomed to a few ways of doing things to specific variables in my workflow:
- Initializing variables with default initializers
- In the property grid, or custom designer, passing the variable as the parameter to an InArgument<T> or an OutArgument<T> of some activity (for OutArgument, often it is called ‘Result’ and we use ‘Set’).
- Use the variable directly in a VBExpression
Based on your accustomed experience to these scenarios, there is a ‘rule’ you can derive: Every use of a variable or argument is explicitly visible somewhere in the designer. The surprise factor is that WorkflowDataContext allows us to invisibly break that rule all over the place. Thereby causing spooky (invisible) action at a distance and so workflows can be created which are ridiculously hard to debug.
So I’d normally not choose to use this technique for data flow, Extensions, or Execution Properties, or regular variable/arguments can be preferred in may cases. And f you are using this for data flow hacks, remember it's maybe a good idea to make that explicit to workflow authors by being visible in the designer.
WorkflowDataContext is still very interesting for its ability to reflect over all the variables and arguments in scope; like our test activity where we printed out the value of all the variables in our workflow. In this case it really didn't matter what variables are available in the workflow, so we avoided depending on specific variables and arguments existing.
Comments
Anonymous
August 09, 2011
Is this only available for CodeActivities?Anonymous
August 10, 2011
@Will - Good point, should be available for all - DataContext property is inherited from ActivityContext, so looks like it is available to all activity types.Anonymous
September 05, 2012
PropertyDescriptor.SetValue() doesn't work, I can't get propertyownerAnonymous
March 08, 2013
Thanks for this. Just what I needed.