Background

I had started to learn Windows Workflow Foundation sometime ago. I prefer to learn a major technology framework through systematic study rather then googling around. However, I found that most well written books and articles were published between 2006-2009, so outdated, particularly missing new features in .NET 4 and 4.5; and a few books published in recent years for WF 4.0 and 4.5 were poorly written. While I generally prefer systematic, dry and abstract study, this time I would make up some wet materials for studying.

Introduction

And this article is focused on InvokeMethod and DynamicActivity.

This is the 2nd article in the series. And source code is available at https://github.com/zijianhuang/WorkflowDemo

Examples in this article are from a test classe: InvokeMethodTest, DynamicActivityTests, AsyncCodeActivityTests.

InvokeMethod

InvokeMethod is handy for making existing functions of objects or types available to workflow through Workflow Designer without programming effort, so you don't need to write new CodeActivity derived classes. And the code examples here are demonstrating the runtime behaviors of InvokeMethod.

While InvokeMethod well supports both instance methods and static methods, the form of calling static methods is simpler naturally. So if you have a large set of static utility functions, it is convenient and straightforward to introduce them into workflow through InvokeMethod.

While InvokeMethod supports RunAsynchronously, however, the activity is not running the method in fire and forget style, and the caller thread still wait for the new thread to finish, even InvokeMethod is among other activities in Sequence.

Example 2

The first case here is expecting a static function of a type, and the second is expecting an instance function. Noted that you have to use a delegate to reference to the instance object.

So basically you may define 0-n properties of InArgument, and 0-n properties of OutArgument, and the Implementation is a Func<Activity> pointer. And the Activity returned in the delegate will be executed by WF.

You can either assign each InArgument property through the Value property of each DynamicActivityPropery object, or assign through a dictionary when invoking the DynamicActivity object.

Generally the execution logic is already defined in an existing Activity object, or a composition of existing Activity objects through Sequence, returned by the Implementation delegate.

If the DynamicActivity has an output, you need to have the Assign activity to assign the value of a variable to the Output argument through ArgumentReference.

Example 2

We may prefer that a DynamicActivity instance returns a strongly typed result.

You don't need to define an OutArgument property, since Result is already defined in DynamicActivity<TResult>.

When using activity Assign in Implementation, the ArgumentName must be "Result".

Hints:

When you are constructing a workflow in Workflow Designer, you are constructing a DynamicActivity, even though YourWorkflow.g.cs generated in YourProject\obj\Debug is derived from class Activity. And when serializing and then deserializing an Activity, the restored class is DynamicActivity.

Remarks:

DynamicActivity cannot be serialized. Thus, if you want to persist a workflow definition, you must not use DynamicActivity inside the workflow.

AsyncCodeActivity

Abstract class AsyncCodeActivity is actually the base class of InvokeMethod, while InvokeMethod is available in WF designer. However, please be aware, there are some tricky things with AsyncCodeActivity as shown in the examples below.

[Fact]
publicvoid TestAsyncDoSomethingInSequence()
{
System.Diagnostics.Debug.WriteLine("TestAsyncDoSomethingInSequence");
var a = new AsyncDoSomethingAndWait();
var s = new System.Activities.Statements.Sequence()
{
Activities = {
new Plus() {X=2, Y=3 },
a,
new Multiply() {X=3, Y=7 },
},
};
var r = WorkflowInvoker.Invoke(s);
System.Diagnostics.Debug.WriteLine("After AsyncDoSomething in Sequence invoke");
//check the log file, the invoker will just run 3 activities one by one, and waiting for a to finish, though the key function of a is running in a new thread
}
[Fact]
publicvoid TestAsyncDoSomethingNotWaitInSequence()
{
System.Diagnostics.Debug.WriteLine("TestAsyncDoSomethingNotWaitInSequence");
var a = new AsyncDoSomethingNotWait();
var s = new System.Activities.Statements.Sequence()
{
Activities = {
new Plus() {X=2, Y=3 },
a,
new Multiply() {X=3, Y=7 },
},
};
var r = WorkflowInvoker.Invoke(s);
System.Diagnostics.Debug.WriteLine("After AsyncDoSomethingNotWait in Sequence invoke");
//check the log file, the invoker will just run 3 activities one by one, and waiting for a to finish, though the key function of a is running in a new thread
System.Threading.Thread.Sleep(1100);
}

If you check the log file, you will see that 3 activities are actually running in sequence in both test cases, regardless that EndExecute() in class AsyncDoSomethingNotWait does not wait. In other words, the WF runtime always wait, and the so called asynchronous execution is blocking. The tricky thing is, this is contradicting against what described in MSDN documentation "Creating Asynchronous Activities in WF".

And in book "Windows Workflow Foundation 4 Cookbook", the non-blocking behavior was apparently confirmed in the digest from page 158 to 161:

If you check the log file, you will see that "After AsyncHttpGet" is printed out after AsyncHttpGet is completely finished.

Example 3 and 4

In the demo code, you will find 2 console app projects: RunWorkflow.csproj on .NET 4.6.1 and RunWF4.csproj on .NET 4. Both reassemble the codes in book "Windows Workflow Foundation 4 Cookbook". And the results are consistent: the executions of AsyncCodeActivity derived classes described in MSDN and the cookbook are actually blocking the caller thread. Apparently AsyncCodeActivity is broken in current releases of .NET 4, .NET 4.5 and .NET 4.6.1, or did I miss something? If you have other idea, please leave a comment.

License

Share

About the Author

I started my IT career in programming on different embedded devices since 1992, such as credit card readers, smart card readers and Palm Pilot.

Since 2000, I have mostly been developing business applications on Windows platforms while also developing some tools for myself and developers around the world, so we developers could focus more on delivering business values rather than repetitive tasks of handling technical details.