I remember at PDC08 when Kenny Wolf was introducing WF4 he said a workflow is kind of like a college student. Most of the time it lays around sleeping (and drinking beer) and occasionally it wakes up (when an assignment is due) and does a pulse of work and then goes back to sleeping and drinking beer. Kenny called this a “pulse of work”, I like to call it a Workflow Episode.
When you run a Workflow the activities will run until one of the following happens
When you write code that uses Workflow Application you provide delegates that are called as the workflow moves through this cycle of events. Sooner or later you realize that your code builds up an understanding (a “contract” if you will) with the workflow that you are running. There is a dialog of sorts that goes on between the host application and the workflow where the host runs the workflow and then waits for idle events to do the next thing.
The more I thought about this pattern the more I wanted a way to make it simpler. So I began to imagine what I wanted from the API and I realized that I wanted a construct that didn't exist yet so I created it by providing extensions to the WorkflowApplication type in the Microsoft.Activities.dll project. This project is an experiment in providing a Task based API for WorkflowApplication. There are no guarantees or promises about this ever becoming part of the product in the future. However we did want to share it with you
My goal for the API was simple.
I wish I had more time to create samples and docs to help you with this. One of the best ways to learn how the API works is by looking at the unit tests.
In this scenario we simply run a workflow that completes. You could do this with WorkflowInvoker today. The advantage here is that there is one model for invoking workflows that works the same way for bookmarks or no bookmarks
Given
When
Then
1: [TestMethod]
2: public void WhenNoBookmarksAndOutArgRunShouldCompleteWithOutArgs()
3: {
4: const int Expected = 1;
5: var workflowApplication = new WorkflowApplication(new EchoArg<int> { InputValue = Expected });
6:
7: // To run it synchronously
8: var result = workflowApplication.RunEpisode(this.DefaultTimeout);
9:
10: // Or asynchronously using a Task
11: // var result = workflowApplication.RunEpisodeAsync(this.DefaultTimeout).Result;
12:
13:
14: Assert.IsInstanceOfType(result, typeof(WorkflowCompletedEpisodeResult));
15:
16: var completedResult = (WorkflowCompletedEpisodeResult)result;
17: Assert.AreEqual(ActivityInstanceState.Closed, completedResult.State);
18: AssertOutArgument.AreEqual(completedResult.Outputs, "Result", Expected);
19: }
2: public void WhenActivityWithBookmarkGoesIdleRunAsyncShouldReturnWorkflowIdleEpisodeResult()
4: var workflowApplication = new WorkflowApplication(new TestBookmark<int> { BookmarkName = "Test" });
5:
6: var result = workflowApplication.RunEpisodeAsync("Test", this.DefaultTimeout).Result;
7:
8: Assert.IsInstanceOfType(result, typeof(WorkflowIdleEpisodeResult));
10: var idleEpisodeResult = (WorkflowIdleEpisodeResult)result;
11: Assert.AreEqual(ActivityInstanceState.Executing, idleEpisodeResult.State);
12: Assert.AreEqual(1, idleEpisodeResult.IdleArgs.Bookmarks.Count);
13: }
There are many, many more tests of the API included in the source for Microsoft.Activities so I encourage you to take a look and see what you think.
Note: Microsoft.Activities.UnitTesting previously had a pre-cursor to this API. Now that I’ve added it to WorkflowApplication I removed the API from Microsoft.Activities.UnitTesting (yes this is a breaking change) Changing to the new API was not too difficult – see the unit tests for examples of what to do.