Microsoft Windows Workflow Foundation 4.0 Cookbook

4 (4 reviews total)
By Andrew Zhu
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Workflow Program

About this book

Windows Workflow Foundation 4.0 (WF) is a significant part of .NET Framework 4.0. WF makes workflow technology available to every single programmer that uses the .NET Framework 4.0. It is easy to create long running and distributed programs using WF with the right knowledge. With this book, you will discover that working with workflows is easy.

This book provides both step-by-step recipes and relevant background information. It covers all the important aspects of Windows Workflow Foundation 4.0. The best thing about this book is that all recipes are based on real-world experiences of Andrew Zhu. Andrew is a global WF and BizTalk technology support engineer for Microsoft. This book covers everything you need to know, when working with workflows. Get to grips with flow control activities, messaging, and transaction processes with easy to understand steps followed by explanations. You will quickly learn to use collection and custom WF activities and WF services.

You will see recipes that illustrate integration of Windows Workflow with other applications such as WPF, ASP.NET, WCF service.Lastly, you will discover how easily you can customize W4 Designer with WF rule engine and others.

Publication date:
September 2010
Publisher
Packt
Pages
272
ISBN
9781849680783

 

Chapter 1. Workflow Program

In this chapter, we will cover:

  • Creating the first WF program: HelloWorkflow

  • Creating a WF program using C# code

  • Initializing a WF program using InArguments

  • Creating a WF program using OutArgument

  • Creating a WF Program using InOutArgument

  • Using Variable in a WF program

  • Running a WF program asynchronously

  • Customizing a MyReadLine activity with Bookmark

  • Converting a WF program instance to XAML

  • Loading up a WF program from an XAML file

  • Testing a WF program with a unit test framework

  • Debugging a WF program

 

Introduction


Considering workflow programs as imperative programs, we need to think of three fundamental things:

  • How to define workflow programs

  • How to build (compile) workflow programs

  • How to execute workflow programs

In WF4, we can define a workflow in either managed .NET code or in XAML. There are two kinds of code workflow authoring styles:

  • Creating a Custom Activity class

  • Creating workflow dynamically in the runtime

There are also two ways to author workflow in XAML:

  • By WF designer (recommended)

  • Typing XML tags manually

Essentially, a workflow program is a .NET program, no matter how we create it.

After defining workflows, we can build workflow applications as we build normal .NET applications.

When it comes to workflow execution, we need to consider three basic things:

  • How to flow data into and out of a workflow

  • How to store temporary data when a workflow is executing

  • How to manipulate data in a workflow

This chapter is going to focus on answering these questions.

Before moving ahead, make sure we have the following installed on our computer:

  • Windows Vista/7 or Windows Server 2008

  • Visual Studio 2010 and .NET framework 4.0

We can also use Windows XP; however, its usage is not recommended.

 

Creating the first WF program: HelloWorkflow


In this task we will create our first workflow to print "Hello Workflow" to the console application.

How to do it...

  1. Create a Workflow Console Application project:

    After starting Visual Studio 2010, select File | New Project. A dialog is presented, as shown in the following screenshot. Under the Visual C# section, select Workflow, and choose Workflow Console Application. Name the project HelloWorkflow. Name the solution Chapter01 and make sure to create a directory for the solution.

  2. Author the workflow program:

    First, drag a Sequence activity to the designer from Toolbox, next drag a WriteLine activity into the Sequence activity. Finally, input "Hello Workflow" in the expression box of the WriteLine activity. We can see in the following screenshot:

  3. Run it:

    Press Ctrl+F5 to run the project without debugging. The result is as shown in the following screenshot:

How it works...

When we press Ctrl+F5, Visual Studio saves the current project, and then it runs the project from the Main method in the Program.cs file.

WorkflowInvoker.Invoke(new Workflow1());

The preceding statement starts the workflow. After the workflow starts running, the WriteLine activity prints the "Hello Workflow" to the Console Application.

The workflow we created in WF Designer is actually an XML file. We can open Workflow1.xaml with an XML editor to check it.

Tip

Right-click on Workflow1.xaml then click Open With…, and choose XML Editor to open Workflow1.xaml as an XML file.

All XAML files will be compiled to .dll or .exe files. That is why when we press Ctrl+F5, the program just runs like a normal C# program.

There's more...

So far, there are no officially published WF4 Designer add-ins for Visual Studio 2008. We need a copy of Visual Studio 2010 installed on our computer to use WF4 Designer, otherwise we can only create workflows by imperative code or by writing pure XAML files.

 

Creating a WF program using C# Code


In this task, we will create the same "HelloWorkflow" function workflow using pure C# code, beginning from a Console Application.

How to do it...

  1. Create a Console Application project:

    Create a new Console Application project under the Chapter01 solution. Name the project HelloCodeWorkflow. The following screenshot shows the Console Application new project dialog:

  2. Add reference to the System.Activities assembly:

    By default, a new Console Application doesn't have reference to the System.Activities assembly, due to which we need to perform this step.

  3. Create workflow definition code:

    Open Program.cs file and change the code present as follows:

    using System.Activities;
    using System.Activities.Statements;
    
    namespace HelloCodeWorkflow {
        class Program {
            static void Main(string[] args) {
                WorkflowInvoker.Invoke(new HelloWorkflow());
            }
        }
    
        public class HelloWorkflow:Activity {
            public HelloWorkflow() {
                this.Implementation = () => new Sequence {
                    Activities = {
                        new WriteLine(){Text="Hello Workflow"}
                    }
                };
            }
        }
    }
    
  4. Run it:

    Set HelloCodeWorkflow as StartUp project and press Ctrl+F5 to run it. As expected, the result should be just like the previous result shown.

How it works...

We use the following namespaces:

using System.Activities;
using System.Activities.Statements;

Because WorflowInvoker class belongs to System.Activities namespace. Sequence activity, WriteLine activity belongs to System.Activities.Statements. namespace.

public class HelloWorkflow:Activity {
    public HelloWorkflow() {
        this.Implementation = () => new Sequence {
            Activities = {
                new WriteLine(){Text="Hellow Workflow"}
            }
        };
    }
}

By implementing a class inherited from Activity, we define a workflow using imperative code.

WorkflowInvoker.Invoke(s);

This code statement loads a workflow instance up and runs it automatically. The WorkflowInvoker.Invoke method is synchronous and invokes the workflow on the same thread as the caller.

There's more

WF4 also provides us a class DynamicActivity by which we can create a workflow instance dynamically in the runtime. In other words, by using DynamicActivity, there is no need to define a workflow class before initializing a workflow instance. Here is some sample code:

public static DynamicActivity GetWF() {
    return new DynamicActivity() {
        Implementation = () => new Sequence() {
            Activities ={
                new WriteLine(){Text="Hello Workflow"}
            }
        }
    };
}
 

Initializing a WF program using InArguments


In this task, we will create a WF program that accepts arguments when initialized in the WF host. In WF4, we can use InArguments to define the way data flows into an activity.

How to do it...

  1. Create a workflow project:

    Create a new Workflow Console Application under the Chapter01 solution. Name the project UseInArgument .

  2. Author the WF program:

    Create a workflow as shown in the following screenshot:

  3. Write code to host the workflow.

    Open the Program.cs file and change the host code as follows:

    using System.Activities;
    using System.Activities.Statements;
    
    namespace UseInArgument {
    class Program {
            static void Main(string[] args) {
                WorkflowInvoker.Invoke(new Workflow1() 
                {
                    FirstName="Andrew",
                    SecondName="Zhu"
                });
            }
        }
    }
  4. Run it:

    Set UseInArgument as StartUp project. Press Ctrl+F5 to build and run the workflow without debugging. The application should run in a console window and print the following message:

How it works...

Consider the following statement from the code we saw in the preceding section:

FirstName="Andrew"

FirstName is an InArgument type, but how can we assign a string to InArgument without any explicit cast? This is because InArgument is declared with an attribute System.ComponentModel.TypeConverterAttribute(System.Activities.XamlIntegration.InArgumentConverter). The class inheritance is shown in the following diagram:

It is the InArgumentConverter that makes assigning a string to an InArgument possible. If we want to know more about TypeConverter, we can check MSDN the reference at http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

There's more

In WF3/3.5, we can pass values to Workflow wrapped in a Dictionary<T> object. This also applies to WF4.

using System.Activities;
using System.Activities.Statements;
using System.Collections.Generic;

namespace UseInArgument {
    class Program {
        static void Main(string[] args) {
            IDictionary<string, object> inputDictionary = 
                new Dictionary<string, object>() 
            { 
                {"FirstName","Andrew"},
                {"SecondName","Zhu"}
            };
            WorkflowInvoker.Invoke(new Workflow1(), 
                                   inputDictionary);
        }
    }
}

If we are creating workflows using imperative code, we can use InArgument in the following way:

public class WorkflowInCode:Activity {
    public InArgument<string> FirstName { get; set; }
    public InArgument<string> SecondName { get; set; }
    public WorkflowInCode() {
        this.Implementation = () => new Sequence() {
            Activities = {
                new WriteLine(){
                    Text=new InArgument<string>(
                            activityContext=>"My name is "+FirstName.Get(activityContext)
                        )
                },
                new WriteLine(){
                    Text=new InArgument<string>(
                            ActivityContext=>SecondName.Get(ActivityContext)
                        )
                }
            }
        };
    }
}
 

Creating a WF program using OutArgument


In this task, we will create a WF program that can return a result to the workflow host.

How to do it...

  1. Create a workflow project:

    Create a new Workflow Console Application under the Chapter01 solution; name the project as UseOutArgument.

  2. Author a workflow:

    Author the workflow shown in the following screenshot. Here are the detailed actions:

    1. Drag a Sequence activity from Toolbox to the designer panel.

    2. In the bottom of the designer panel, click the Arguments button, and click Create Argument to create an OutArgument string named OutMessage.

    3. Drag two WriteLine activities from Toolbox into the Sequence activity and fill the textboxes with "Start…" and "End" respectively.

    4. Drag an Assign activity from Toolbox to the designer panel. Fill the right expression box with OutArgument as OutMessage, whereas fill the right expression box with the following string: This is a message from workflow.

  3. Write code to host the workflow:

    Open Program.cs file and change the host code as follows:

    using System;
    using System.Activities;
    using System.Collections.Generic;
    
    namespace UseOutArgument {
        class Program {
            static void Main(string[] args) {
                IDictionary<string,object> output=
                    WorkflowInvoker.Invoke(new Workflow1());
                Console.WriteLine(output["OutMessage"]);
            }
        }
    }
    
  4. Run it:

    Set UseOutArgument as Startup project. Press Ctrl+F5 to build and run the workflow without debugging. The application should run in a console window and print the message as shown in the next screenshot:

How it works...

Look at the following code snippet:

IDictionary<string,object> output=
     WorkflowInvoker.Invoke(new Workflow1());
Console.WriteLine(output["OutMessage"]);

OutMessage is the name of OutArgument we defined in Workflow1.xaml. the WorkflowInvoder.Invoke method will return a IDictionary type object.

There's more...

There is a third type of workflow argument: InOutArgument. It is a binding terminal that represents the flow of data into and out of an activity. In most cases, we can use InOutArgument instead of InArgument and OutArgument. But there are still some differences—for example, we cannot assign a string to InOutArgument, while it is allowed to assign a string to InArgument directly in the host program.

 

Creating a WF program using InOutArgument


In this task, we will create a WF program using InOutArgument. This type of argument is used to receive values and is also used to pass values out to the caller (WF host).

How to do it…

  1. Create a workflow project:

    Create a new Workflow Console Application under the Chapter01 solution and name the project as UseInOutArgument.

  2. Author a workflow:

    Create an InOutArgument type argument: InOutMessage. Author a WF program as shown in the following screenshot. In the Assign activity textbox, type InOutMessage = "Now, I am an OutMessage".

  3. Write code to host the workflow:

    Open the Program.cs file and alter the code as shown:

    using System;
    using System.Activities;
    using System.Activities.Statements;
    using System.Collections.Generic;
    
    namespace UseInOutArgument{
        class Program{
            static void Main(string[] args){
                IDictionary<string, object> input =
                    new Dictionary<string, object>() 
                { 
                    {"InOutMessage","Now, I am InMessage"}
                };
                IDictionary<string,object> output=
                    WorkflowInvoker.Invoke(new Workflow1(),input);
                Console.WriteLine(output["InOutMessage"]);
            }
        }
    }
    
  4. Run it:

    Set UseInOutArgument as Startup project. Press Ctrl+F5 to build and run the workflow without debugging. The application should run in a console window and print the message as shown in the following screenshot:

How it works...

The following code block initializes the InArgument value:

IDictionary<string, object> input =
                new Dictionary<string, object>() 
            { 
                {"InOutMessage","Now, I am InMessage"}
            };

This statement will run the workflow program with the input dictionary.

IDictionary<string,object> output=
                WorkflowInvoker.Invoke(new Workflow1(),input);

The string Now, I am InMessage is printed by the workflow. The string Now, I am an OutMessage is a message altered in the workflow and passed to the host and then printed by the host program.

There's more...

We cannot assign a string to InOutArgument directly, and the following style of parameter initialization is not allowed:

IDictionary<string, object> output =
    WorkflowInvoker.Invoke(new Workflow1() 
    { 
        InOutMessage="Now,I am InMessage"
    });

See Also

  • Creating a WF program using OutArgument

  • Initializing a WF program using InArguments

 

Using Variable in a WF program


We can use Variable temporarily to store a value when a WF program is running. In this task, we will create a WF program that prints five numbers to the console in a loop. We will use the NumberCounter variable as a number counter.

How to do it...

  1. Create a workflow project:

    Create a new Workflow Console Application under the Chapter01 solution and name the project as UseVariable.

  2. Author a workflow:

    Add a Sequence activity, click the Sequence activity, create an Int32 NumberCounter variable, and set its Scope to Sequence. Then, author the workflow as shown in the following screenshot. In the second Assign activity type NumberCounter=NumberCounter+1.

  3. Run it:

    Set UseVariable as Startup project. Press Ctrl+F5 to build and run the workflow without debugging. The application should run in a console window and print the following message:

How it works...

To make the workflow logic easy to understand, translate the workflow into C# code. It will look like:

int NumberCounter = 0;
do 
{
    Console.WriteLine(NumberCounter);
    NumberCounter++;
}while (NumberCounter <= 5);

While we can use arguments to flow data into and out of a workflow, we use Variable to store data in a workflow. Every variable has its scope, and can be accessed by activities within its scope. Variable in WF4 is pretty much like variables in imperative language such as C#.

There's more...

Please note that we cannot access to the workflow variables from the outside host. WF4 variables are designed for sharing data inside the workflow instance. We can use Bookmark to access the workflow from the outside host.

See Also

  • Customizing a MyReadLine activity with Bookmark

 

Running a WF program asynchronously


In the previous tasks, we used the WorkflowInvoker.Invoke method to start a workflow instance on the same thread as the main program. It is easy to use; however, in most real applications, a workflow should run on an independent thread. In this task, we will use WorkflowApplication to run a workflow instance.

How to do it...

  1. Create a workflow project:

    Create a new Workflow Console Application under the Chapter01 solution and name the project as UseWorkflowApplication.

  2. Author a workflow:

    In the opening Workflow1.xaml designer, click on Arguments, create two Int32 InArguments for Number1 and Number2. Create an Int32 OutArgument for Result. Add an Assign activity to the workflow designer panel. In the Assign activity, type Result=Number1+Number2.

  3. Write code to host the workflow:

    Open Program.cs file and change code as follow:

    using System;
    using System.Activities;
    using System.Activities.Statements;
    using System.Threading;
    using System.Collections.Generic;
    
    namespace UseWorkflowApplication{
        class Program{
            static void Main(string[] args){
                AutoResetEvent syncEvent = 
                   new AutoResetEvent(false);
                IDictionary<string, object> input = 
                   new Dictionary<string, object>()
                {
                    {"Number1",123},
                    {"Number2",456}
                };
                IDictionary<string,object> output=null;
                WorkflowApplication wfApp = 
                   new WorkflowApplication(new Workflow1(),input);
                wfApp.Completed = 
                delegate(WorkflowApplicationCompletedEventArgs e)
                {
                    Console.WriteLine("Workflow thread id:"+
                            Thread.CurrentThread.ManagedThreadId);
                    output = e.Outputs;
                    syncEvent.Set();
                };
                wfApp.Run();
                syncEvent.WaitOne();
                Console.WriteLine(output["Result"].ToString());
                Console.WriteLine("Host thread  id:"+Thread.CurrentThread.ManagedThreadId);
            }
        }
    }
  4. Run it:

    Set UseWorkflowApplication as Startup project. Press Ctrl+F5 to build and run the workflow without debugging. The application should run in a console window and print the following message:

How it works...

The function of this workflow is adding two InArgument Numbers and assigning the result to an OutArgument Result.

AutoResetEvent syncEvent = new AutoResetEvent(false);

As the workflow thread runs simultaneously with the caller thread, the caller thread may terminate before the workflow thread. To prevent this unexpected program quit, we need to use AutoResetEvent to synchronize caller and workflow thread.

syncEvent.WaitOne();

The caller thread will wait there, until syncEvent is set.

wfApp.Completed = 
delegate(WorkflowApplicationCompletedEventArgs e)
{
   output = e.Outputs;
   syncEvent.Set();
};

When the workflow completes, syncEvent.Set() is invoked. After that, the caller can continue running to its end.

Another thing we should be aware of is how we get the result when the workflow ends. Unlike the WorkflowInvoker.Invoker method, in a WorkflowApplication-style caller, we get dictionary output from WorkflowApplicationCompletedEventArgs's Outputs property; see the preceding code snippet.

 

Customizing a MyReadLine activity with Bookmark


By using InArgument, OutArgument, and InOutArgument, we can flow data into the workflow when it starts and out of the workflow when it ends. But how can we pass data from the caller into the workflow when it is executing?—Bookmark will help us to do this. In this task, we will create a MyReadLine activity using a bookmark.

How to do it...

  1. Create a workflow project:

    Create a new Workflow Console Application under the Chapter01 solution and name the project as UseBookmark. Next, add a code file to this project and name the file as MyReadLineActivity. We can see this in the following screenshot:

  2. Customize the activity with Bookmark:

    Fill the opening MyReadLineActivity.cs file with the following code:

    using System.Activities;
    namespace UseBookmark{
        public class MyReadLine : NativeActivity<string>{
            [RequiredArgument]
            public InArgument<string> BookmarkName { get; set; }
            protected override void Execute(
                NativeActivityContext context)
            {
                context.CreateBookmark(BookmarkName.Get(context),
                         new BookmarkCallback(OnResumeBookmark));
            }
            protected override bool CanInduceIdle
            {
                get
                {
                    { return true;}
                }
            }
            public void OnResumeBookmark(
                NativeActivityContext context,
                Bookmark bookmark,
                object obj)
            {
                Result.Set(context, (string)obj);
            }
        }
    }
    

    Save the file and press F6 to build the project so that the activity will appear in the WF designer activity toolbox.

  3. Author a workflow:

    Open Workflow1.xaml and author the workflow as shown in the following screenshot:

  4. Write code to host the workflow:

    Open Program.csfile and change the code as follows:

    using System;
    using System.Linq;
    using System.Activities;
    using System.Activities.Statements;
    using System.Threading;
    
    namespace UseBookmark{
        class Program{
            static void Main(string[] args)
            {
                AutoResetEvent syncEvent = 
                    new AutoResetEvent(false);
                string bookmarkName="GreetingBookmark";
                WorkflowApplication wfApp = 
                    new WorkflowApplication(new Workflow1()
                {
                    BookmarkNameInArg=bookmarkName
                });
                wfApp.Completed = delegate(
                    WorkflowApplicationCompletedEventArgs e)
                {
                    syncEvent.Set();
                };
                wfApp.Run();
                wfApp.ResumeBookmark(bookmarkName, 
                    Console.ReadLine());
                syncEvent.WaitOne();
            }
        }
    }
    
  5. Run it:

    Set UseBookmark as Startup project. Press Ctrl+F5 to build and run the workflow without debugging. The application should run in a console window and print the message as shown in the following screenshot:

How it works...

In the code shown in the second step, we create a class inherited from NativeActivity. NativeActivity is a special abstract activity that can be used to customize complex activities; we will talk about it more in Chapter 5, Custom Activities.

context.CreateBookmark(BookmarkName.Get(context),
        new BookmarkCallback(OnResumeBookmark));

By this statement, the WF context creates a Bookmark with arguments BookMarkName and BookMarkCallback. When the wfApp.ResumeBookmark method is called, the OnResumeBookmark that was defined in the Customized Activity body will be executed.

protected override bool CanInduceIdle{
    get
    {
        { return true;}
    }
}

This is a built-in property that indicates whether the customized activity can cause the workflow to become idle; the default value is false.

Consider the following code snippet of step 3:

wfApp.ResumeBookmark(bookmarkName, 
                Console.ReadLine());

When this statement is executed, the OnResumeBookmark method defined in the MyReadLine activity will be called and the method will accept the value passed via Console.ReadLine().

 

Converting a WF program instance to XAML


In real applications, we would like to write and test WF programs in imperative code, while storing, running, and transmitting workflow as an XAML string or file. In this task, we will convert a WF program instance to an XAML string.

How to do it...

  1. Create a workflow project:

    Create a new Workflow Console Application under the Chapter01 solution and name the project ConvertWFInstanceToXML. Delete the Workflow1.xaml file that is created by default.

  2. Write code to create the workflow and its host:

    Open Program.cs file and change the code as follows:

    using System;
    using System.Activities;
    using System.Activities.Statements;
    using System.Text;
    using System.Xaml;
    using System.Activities.XamlIntegration;
    using System.IO;
    
    namespace ConvertWFObjectToXML {
        class Program {
            static void Main(string[] args) {
                //Create a Workflow instance object
            ActivityBuilder ab = new ActivityBuilder();
                ab.Implementation = new Sequence() 
                {
                    Activities =
                    {
                        new WriteLine{Text="Message from Workflow"}
                    }
                };
    
                //Convert Workflow instance to xml string
                StringBuilder sb = new StringBuilder();
                StringWriter sw = new StringWriter(sb);
                XamlWriter xw = 
                    ActivityXamlServices.CreateBuilderWriter(
                    new XamlXmlWriter(sw, 
                                      new XamlSchemaContext()));
                XamlServices.Save(xw, ab);
                Console.WriteLine(sb.ToString());
            }
        }
    }
  3. Run it:

    Set ConvertWFInstanceToXML as Startup project. Press Ctrl+F5 to build and run the workflow without debugging. The application should run in a console window and print the message as shown in the following screenshot:

Consider the following XML string reformatted from the screenshot:

<?xml version="1.0" encoding="utf-16"?>
<Activity  x:Class="{x:Null}" 
                  xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" 
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
       <Sequence>
            <WriteLine Text="Hello" />
       </Sequence>
</Activity>

How it works...

Consider the following code line:

XamlServices.Save(xw, ab);

XamlServices provides services for the common XAML tasks of reading XAML and writing an object graph, or reading an object and writing out an XAML file. This statement reads an ActivityBuilder object and writes XAML to an XamlWriter object.

We use ActivityBuilder as an activity wrapper so that the output XAML is a loadable workflow. In other words, if we save, say, a Sequence activity to an XamlWriter directly, then the output XML workflow will be unloadable for further use.

 

Loading up a WF program from an XAML file


In this task, we will run a WF program by loading it from an XAML file.

How to do it...

  1. Create a workflow project:

    Create a new Workflow Console Application under the Chapter01 solution and name the project as LoadUpWorkflowFromXML.

  2. Author a workflow:

    Author the Workflow1.xaml file; this workflow will print a string to console as shown in the following screenshot:

  3. Create code to load up the workflow instance from an XAML string:

    Open Program.cs file and change code as follow:

    using System;
    using System.Activities;
    using System.Activities.Statements;
    using System.IO;
    using System.Collections;
    using System.Text;
    using System.Activities.XamlIntegration;
    
    namespace LoadUpWorkflowFromXML {
        class Program {
            static void Main(string[] args) {
                string filePath=           @"C:\WF4Cookbook\Chapter01\LoadUpWFFromXML\Workflow1.xaml";
                string tempString="";
                StringBuilder xamlWFString = new StringBuilder();
                StreamReader xamlStreamReader = 
                    new StreamReader(filePath);
                while (tempString != null){
                    tempString = xamlStreamReader.ReadLine();
                    if (tempString != null) {
                        xamlWFString.Append(tempString);
                    }
                }
                Activity wfInstance = ActivityXamlServices.Load(
                    new StringReader(xamlWFString.ToString()));
                WorkflowInvoker.Invoke(wfInstance);
            }
        }
    }
    

    Tip

    We may need to change the file path according to our real environment.

  4. Run it:

    Set LoadUpWorkflowFromXML as Startup project. Press Ctrl+F5 to build and run the workflow without debugging. The application should run in a console window and print the message as shown in the following screenshot:

How it works...

We use the following code block to read a workflow XML string from file and store the string in xamlWFString:

string filePath=           @"C:\WF4Cookbook\Chapter01\LoadUpWFFromXML\Workflow1.xaml";
string tempString="";
StringBuilder xamlWFString = new StringBuilder();
StreamReader xamlStreamReader = 
new StreamReader(filePath);
while (tempString != null)
{
    tempString = xamlStreamReader.ReadLine();
    if (tempString != null) 
    {
        xamlWFString.Append(tempString);
    }
}

Then, using the following statement, ActivityXamlServices reads the XML workflow and builds up a workflow object graph:

Activity wfInstance = ActivityXamlServices.Load(
                new StringReader(xamlWFString.ToString()));
 

Testing a WF program with a unit test framework


In this task, we will create a Test Project to do unit testing for a WF program.

How to do it...

  1. Add a Test Project to the solution:

    Add a Test Project to the Chapter01 solution and name the project as UnitTestForWFProgram as shown in the following screenshot:

  2. Add a workflow file to the Test Project:

    Add a workflow activity to this project. Right-click the newly created Test Project, then go to Add | New Items… | Workflow | Activity and name the activity as WorkflowForTest.xaml. In the opening WF designer, create an OutArgument as OutMessage. Next, drag an Assign activity to the Designer panel and assign the string "Test Message" to the OutMessage argument as shown in the following screenshot:

    Tip

    In WF4, workflow is actually an Activity class. We could see "Workflow" as a conception from a macroeconomic viewpoint, while considering "Activity" as a development concept.

  3. Create unit test code:

    Open the UnitTest1.cs file and fill the file with following code:

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.Activities;
    
    namespace UnitTestForWFProgram {
        [TestClass]
        public class UnitTest1 {
            [TestMethod]
            public void TestMethod1() {
                var output = 
                    WorkflowInvoker.Invoke(new WorkflowForTest());
                Assert.AreEqual("Test Message", 
                                output["OutMessage"]);
            }
        }
    }
  4. Run it:

    Set UnitTestForWorkflow as Startup project. Press Ctrl+F5 to build and run the test without debugging as shown in the following screenshot:

How it works...

In the preceding code snippet, [TestClass] indicates it is a unit test class, whereas [TestMethod] indicates a test method. When the Test Project runs, the test method will be executed automatically.

There's more...

In real application development, we can also create a separate Unit Test project and add a reference to the target project.

 

Debugging a WF program


In this task, we will debug a WF program.

How to do it...

  1. Create a workflow project:

    Create a new Workflow Console Application project under the Charpter01 solution. Name the project as DebugWFProgram. In the opening WF designer panel, author a workflow as shown in the following screenshot:

  2. Create workflow host code:

    Open Program.cs file and change the code to:

    using System.Activities;
    using System.Activities.Statements;
    
    namespace DebugWFProgram{
        class Program{
            static void Main(string[] args){
                WorkflowInvoker.Invoke(new Workflow1()
                { 
                    InMessage="In Message"
                });
            }
        }
    }
    
  3. Set a debug break point:

    Right-click an activity and select Breadpoint | Insert Breakpoint to add debug break point.

  4. Debug it:

    Press F5 to debug the WF Program; we can refer the following screenshot:

There's more...

We can also debug an XAML workflow. Open Workflow with the XML editor, insert some breakpoints, then press F5; we will see the breakpoints as shown in the following screenshot:

About the Author

  • Andrew Zhu

    Andrew Zhu is a technology support engineer at Microsoft (Microsoft Globe Tech Support Center) in Shanghai. Over the past few years, he has helped developers and his customers to develop WF, BizTalk, IIS, and ASP.NET applications.

    Browse publications by this author

Latest Reviews

(4 reviews total)
Would not have bought their books if I had known about the spam
Exactly what I was looking for and is included in the bundled offer
The books I have purchased are "MICROSOFT WINDOWS WORKFLOW FOUNDATION 4.0 COOKBOOK" and "Programming Windows Workflow Foundation: Practical WF Techniques and Examples using XAML and C#" worth reading for beginners.