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

Comments (0)

Skip to main content