WorkflowChanges: Reusing the modified XOML workflow template

Please Note: This post is specific to .Net Framework 3.5 and VS2008

In this scenario we have an existing WF workflow instance that we will change using the WorkflowChanges class. Our objective would to be to retain these changes (using XOML) and use this new WF activity structure to create new WorkflowInstance(s).

So, let’s look at the code. For easy understanding we will break this up into 5 simple steps.

1. Create a XOML only workflow as a starting point. This would have an associated assembly containing the functionality that we will load later.

2. Host the WF runtime in a console app and create a workflow instance.

3. Make changes to the workflow instance

4. Store the new (modified) workflow template

5. and finally, create new instances from this new template

 

Step1: Create a XOML only workflow

a) Create a Class Library project and create your own type that derives from SequenceActivity and add the required code beside elements and compile it. Refer to WorkflowProject1 in the attached code.

 

Note: make sure that the code handlers are public otherwise you won’t be able to bind to it from your markup (XOML workflow).

 

namespace WorkflowProject1

{

    public partial class Activity1 : System.Workflow.Activities.SequenceActivity{

    public void codeActivity1_ExecuteCode(object sender, EventArgs e)

    {

        Console.WriteLine("Executing Code Activity1");

    }

    public void codeActivity2_ExecuteCode(object sender, EventArgs e)

    {

        Console.WriteLine("Executing Code Activity2");

    }

}

}

b) Create a XOML only workflow (TextFile1.xoml) as shown below. Note that the type created above is the root activity in this case and refers to the assembly created above. Also note that we have only two CodeActivities in this template.

 

<ns0:Activity1 x:Name="Activity12" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ns0="clr-namespace:WorkflowProject1;Assembly=WorkflowProject1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">

  <CodeActivity x:Name="codeActivity1" ExecuteCode="{ActivityBind Name=Activity12, Path=codeActivity1_ExecuteCode}" />

  <CodeActivity x:Name="codeActivity2" ExecuteCode="{ActivityBind Name=Activity12, Path=codeActivity2_ExecuteCode}" />

</ns0:Activity1>

Step2: Host the WF runtime in a console app and create a workflow instance

Create a Console app (ConsoleHostApplication1) and add the WorkflowProject1 assembly as a reference.

Before we can create an instance of the above XOML workflow, we need to tell the WorkflowRuntime where to find the associated functionality (for the activities being used). For this we need to add a TypeProvider runtime service. Add the following to the Program.cs (you would need some more WF hosting code that you would find in the attached code sample)

 

Note: We do not need the TypeProvider if the assembly is deployed to GAC.

 

WorkflowRuntime workflowRuntime = new WorkflowRuntime();

// Add the Assembly that contains the codebehind

// to a TypeProvider and register the TypeProvider with

// the WorkflowRuntime.

TypeProvider t = new TypeProvider(workflowRuntime);

t.AddAssembly(typeof(WorkflowProject1.Activity1).Assembly);

workflowRuntime.AddService(t);

//run XOML workflow

XmlTextReader reader = new XmlTextReader(@"TextFile1.xoml");

WorkflowInstance instance = workflowRuntime.CreateWorkflow(reader);

instance.Start();

 

Related reading for Step 2:

Loading Workflow Models in WF

Using Workflow Markup

Using Rules with Workflow Markup

Using Custom Activities with Workflow Markup

 

 

Step3: Make changes to the workflow instance

We do this with the help of the WorkflowChanges class in Framework. This lets us create a transient workflow and make changes to it. Read more about it in the “Related reading” section for this step.

In our case we add another CodeActivity to the existing activity tree.

** You may want to provide a designer for editing the XOML. Please find related resources/sample code at the end of this post.

 

 //Suspend before changing

 instance.Suspend("Changing");

   

 //making changes

 WorkflowChanges changes = new WorkflowChanges(instance.GetWorkflowDefinition());

 changes.TransientWorkflow.Activities.Add(new CodeActivity("codeActivity3"));

 ActivityBind bind = new ActivityBind("Activity12", "codeActivity2_ExecuteCode");

 changes.TransientWorkflow.Activities["codeActivity3"].SetBinding(CodeActivity.ExecuteCodeEvent, bind);

 instance.ApplyWorkflowChanges(changes);

Related reading for Step 3:

Workflow Changes Overview

Workflow Changes to Rule Conditions

Using Workflow Changes in Workflows

How to: Apply Workflow Changes to Workflows

Dynamic Update from Host - Sample

Changing Rules - Sample

 

 

Step4: Store the modified template for future use

Note that we had suspended the workflow instance in step3. We take the structure of this paused (and now modified) workflow instance and store it in memory. Later we show it on the Console as output. You can just as well add some simple code to store this in a database table and even associate some version numbers to these templates. The key to this step is the WorkflowMarkupSerializer. Read more about it in the “Related reading” section for this step

 

//get the new workflow template/blueprint, optionally save it to a database with new version number for the template

WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();

StringBuilder sb = new StringBuilder();

XmlWriter xwriter = XmlTextWriter.Create(sb);

serializer.Serialize(xwriter, instance.GetWorkflowDefinition());

Console.ForegroundColor = ConsoleColor.Cyan;

Console.WriteLine(sb.ToString() + "\n"); //show modified workflow template, notice the additional Code activity in the console output

Console.ForegroundColor = ConsoleColor.White;

 

//or write it to a file so that we can use it later

XmlWriter xwriter2 = XmlTextWriter.Create(@"C:\NewWorkflowTemplate.xoml");

serializer.Serialize(xwriter2, instance.GetWorkflowDefinition());

 

//resume the original workflow instance

instance.Resume();

 

Now let’s RUN the console app to see the changed workflow structure. Notice the additional CodeActivity that we added dynamically “codeActivity3” and the third CodeActivity output (highlighted).

 

Output_step4 

 

Related reading for Step 4:

Serialization Overview

How to: Serialize Workflows

Serializing Custom Activities

Workflow Serialization Sample

 

 

Step5: Create new workflow instances from the new template

Create another Console app (ConsoleHostApplication2) and add the WorkflowProject1 assembly as a reference. Here we write the usual WF hosting code (see attached code sample for specifics).

As before we create a TypeProvider to associate the assembly, and then use the XMLReader overload of the WorkflowRuntime.CreateWorkflow() method to create an instance for the new workflow blueprint.

 

TypeProvider t = new TypeProvider(workflowRuntime);

t.AddAssembly(typeof(WorkflowProject1.Activity1).Assembly);

workflowRuntime.AddService(t);

//run XOML workflow

XmlTextReader reader = new XmlTextReader(@"C:\NewWorkflowTemplate.xoml");

WorkflowInstance instance = workflowRuntime.CreateWorkflow(reader);

instance.Start();

 

Upon running this console app, you would see that there is a third CodeActivity output (highlighted), whereas the original workflow template from Step 1b had only two CodeActivities.

 

Output_step5

 

 

Download Code

 

XOML Workflow designer re-hosting resources:

Viewing/Editing Workflow's in XML (WF Beta1 codebase) **

WFPad for Windows Workflow Foundation Beta 2 (with code) **

and also..

WF Scenarios Guidance: Workflow Designer Re-Hosting

Windows Workflow Foundation: Everything about Re-Hosting the Workflow Designer

 

 

                               

SampleCode.zip