The Problem Solver

Tell me and I will forget
Show me and I will remember
Involve me and I will understand
- Confucius -

June 2009 - Posts

A recent article I wrote for the MSDN website just went live.

Read is here.

Posted by Maurice | with no comments

Passing parameters into a workflow is similar in WF4 as it was in WF3.

In both case a Dictionary<string, object> is passed in when creating the workflow instance object. Alternatively when using the WF4 WorkflowInvoker you can pass it into the Invoke() method.

This is quite simple but I have never been a fan of the very loose coupling here. basically on the outside you have to create a dictionary with as key something inside of the workflow. No compile time checking. No improvement where, unless you consider the somewhat easier unit testing with the WorkflowInvoker.

The actual behavior between WF3 and WF4 is quite different however.

In Wf3 if you pass in a key that doesn’t match up you get a System.Reflection.TargetInvocationException with a System.InvalidOperationException inner exception with the text: “This operation can not be performed at runtime.”. Great help in debugging Sad

In WF4 on the other hand you get no error at all. The value is just ignored so it is even harder to detect typos. Well I guess not every change can be an improvement. And just in case you where thinking “But a unit test will take care of that right?” keep in mind that workflows tend to be complex and long running things not very well suited to unit testing.

 

Starting a workflow with a parameter:

Shared Sub Main()
    Dim inArgs As New Dictionary(Of String, Object)
    inArgs("Name") = "Maurice"
 
    Dim myInstance As WorkflowInstance = New WorkflowInstance(New Sequence1(), inArgs)
    myInstance.OnCompleted = AddressOf OnWorkflowCompleted
    myInstance.OnAborted = AddressOf OnWorkflowAborted
    myInstance.OnUnhandledException = AddressOf OnWorkflowUnhandledException
    myInstance.Run()
 
    syncEvent.WaitOne()
End Sub

 

So where is this Name value actually stored?

Well in WF3 that would have been stored in a property, possibly a dependency property, or a field. The types must match, exactly.

In WF4 the notion of properties and fields is gone. No more dependency properties to tie everything together. So instead we have Variables and Arguments. Basically a variable is just like a variable in a function, only here it holds some value for an activity. Big difference is that a Variable is in scope, i.e. visible, to all nested activities as well. So basically it is a way of sharing state between different activities.

To allows us to pass data into a workflow, or get something out, we need to use an Argument.

image

In this case I have defined two arguments, the Name to pass into the workflow and the Greeting that will be the result of the workflow.

Getting the result out is done in the OnCompleted delegate. This delegate is passed a WorkflowCompletedEventArgs which in turn contains another dictionary with all out argument.

 

Shared Sub OnWorkflowCompleted(ByVal e As WorkflowCompletedEventArgs)
    Dim greeting As String = e.Outputs("Greeting")
    Console.WriteLine(greeting)
 
    syncEvent.Set()
End Sub

Again not very different from WF3 where we used the WorkflowCompleted event and the OutputParameters dictionary from the WorkflowRuntime.

 

Conclusion

While there are quite a few differences in the details the basic approach remains the same. And as the old approach with dictionaries was rather error prone I consider this a missed chance Sad

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 2 comment(s)
Filed under: , , , , ,

With WF3 there was a central workflow runtime environment called the WorkflowRuntime and used to manage the lifetime of workflow instances. In WF4 this central class no longer exists and we manage individual workflows.

To compare the two, this is what a minimal console application looks like in WF3. It basically runs a workflow and prints a message when done:

static void Main(string[] args)
{
    using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
    {
        workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
        {
            Console.WriteLine("The workflow has completed.");
        };
 
        WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));
        instance.Start();
 
        Console.WriteLine("Waiting for the workflow to complete.");
        Console.ReadLine();
    }
}

 

The same functionality in WF4 looks very different:

static void Main(string[] args)
{
    WorkflowInstance myInstance = new WorkflowInstance(new Sequence1());
 
    myInstance.OnCompleted += delegate(WorkflowCompletedEventArgs e)
    {
        Console.WriteLine("The workflow has completed.");
    };
    myInstance.Run();
 
    Console.WriteLine("Waiting for the workflow to complete.");
    Console.ReadLine();
}

 

The most important thing to note is that there is no longer a WorkflowRuntime object, instead we create a new WorkflowInstance object passing in a workflow instance (compared to a workflow type before).

 

There are some benefits as well as drawbacks to not having a WorkflowRuntime.

The main benefit is that it makes creating and running workflows much easier. No longer do we need to keep track of a singleton and use that to do everything. Another benefit is we can register event handler per workflow instance, makes it easier to do different things for different types of workflows. In fact the OnCompleted isn’t an event but a delegate so you can add only a single handler.

The drawback is that general configuration. Like recording workflow errors, tracking execution or workflow persistence must be done for each instance while before we could just do it on the workflow runtime.

Some things haven’t changed. In both WF3 and WF4 running the workflow is done asynchronously so we need to wait, in both cases done using a Console.ReadLine().

 

Synchronous execution

With WF3 it was hard to have a workflow execute synchronously. Basically we had to add a different scheduler to the workflow runtime and explicitly tell the scheduler to execute the workflow. Of course this would affect every workflow created through the same WorkflowRuntime.

With WF4 there is a much easier model using the WorkflowInvoker class. All we need to do is call WorkflowInvoker.Invoke() passing in the workflow object and the workflow will execute synchronously on the same thread. Not only very useful for running the odd workflow but also for unit testing as any WorkflowElement, the base class for all activities, can be executed this way.

static void Main(string[] args)
{
    WorkflowInvoker.Invoke(new Sequence1());
 
    Console.WriteLine("The workflow has completed.");
    Console.ReadLine();
}

Nice right!

 

Enjoy.

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 2 comment(s)
Filed under: , , , ,

As you may have heard Windows Workflow Foundation 4 is not an upgrade from Windows Workflow Foundation 3 (or 3.5). The version numbers might suggest that the previous version was quite mature but in fact it refers to the version of the .NET framework. In fact Windows Workflow Foundation 3 was the first version and 3.5 added only very few features and some bug fixes. And as WF 4 is a complete rewrite we should approach it as a new product and forget just about everything we already know about WF 3.

That might be a bit surprising, after all we still have activities and a workflow runtime right?

Well wrong actually!

There no longer is a class named WorkflowRuntime. Creating workflows is done by creating a WorkflowInstance object. This object is raises events for that workflow and only for that specific workflow.

 

But surely there is still an Activity base class?

Yes there is. But this is a completely new class that has nothing to do with the class Activity that was used with WF 3. In fact the new Activity class is in a new assembly and namespace, the full name is System.Activities.Activity in the assembly System.Activities.dll. The “old” activity class was System.Workflow.ComponentModel.Activity in the System.Workflow.ComponentModel.dll assembly. The old class still exists in .NET 4 so old workflows will still run but the new classes have no relation with the old classes.

So basically forget all you knew about the behavior of WF 3 Sad

 

So lets take a brief look at creating a new workflow 4 style workflow.

When we start Visual Studio 2010 and select File/New/Project we get the new project tab. Select the Workflow node and you will see the four new project types

image

More about the different types in another blog post. For now just select the “Sequential Workflow Console Application”.

What we end up with is a simple project with just 2 files, the program.cs and the Sequence1.xaml. The latter contains the workflow. Note there is no choice between code or xaml workflows, the designer only works with xaml based workflows. Also note that the extension is no longer XOML but has changed to XAML.

The Sequence1.xaml workflow is still empty and looks like this:

image

When we open the Toolbox we see the new collection of basic activities. These are also completely new. For now just drag a WriteLine activity onto the Sequence in the designer. The goal of the WriteLine activity is just what the name suggest: write a line of text to some form of output stream. When we open the property sheet with the WriteLine activity selected we see there are just three properties: the DisplayName, the Text and the TextWriter.

image

The DisplayName is actually the name used on the design surface and used during the design of the workflow.

The Text property contains the text we want to print. In the property sheet we see a textbox with the text “<Enter an expression>”. This really is an expression editor where you can type in code. So if you want to enter a simple fixed text you must use quotation marks around it. This must always be a valid Visual Basic statement. yes that isn’t a typo Visual Basic, not C#, even if you are developing a C# project.
So in this case just add “Hello Workflow 4” including the quotation marks.

The TextWriter property is the text stream to write the text to. This is also an expression, just like the Text, and is optional. When not specified the text will be written to Console.Out which will do fine for now.

image

These expression windows in VB are completely new and a big change from WF3. Where we previously had to bind to property binding or something similar to add custom code we can now type it directly into the designer.

image

With this in place we are ready to run the workflow. Press Ctrl+F5 and with a bit of luck you should see the output appear in the console window. You might need a bit of luck because it appears the current bits are still a bit unstable and on my laptop running Windows 7 the designer tends to lock up quite a bit Sad

In the next post more about how the workflow is created and started at runtime.

Posted by Maurice | 5 comment(s)
Filed under: , , , ,