Dynamic Update in WF4.5: Sample using a re-hosted Workflow Designer


Introduction

WF 4.5 added Versioning and Dynamic Updates to workflows.

The dynamic update process consists in 2 main steps:

  1. Defining the update, in terms of changes to the workflow definition. The output of this step is an Update Map
  2. Applying the Update Map to persisted workflow instances

Step A. above takes place in 3 substeps:

  1. Calling DynamicUpdateServices.PrepareForUpdate(Activity), where activity is the root activity of the workflow definition
  2. Making in-memory changes to the workflow definition
  3. Calling DynamicUpdateServices.CreateUpdateMap(Activity). This creates an update map, which is typically persisted, to be used sometime later to carry out step B. above.

Under the hood

Steps 1. – 3. above must be applied to the same in-memory workflow definition. This is because DynamicUpdateServices.PrepareForUpdate(activity) prepares that specific in-memory workflow definition for update, so the following call to CreateUpdateMap() must happen on the same in-memory instance.

PrepareForUpdate() annotates the in-memory workflow definition with information that will be used at a later time, when CreateUpdateMap() is called, to compute the delta. This is logically equivalent to creating an internal cloned workflow definition.

Note: PrepareForUpdate() and CreateUpdateMap() have overloads that take an ActivityBuilder as an argument, instead of an Activity. This simplifies the usage in conjunction with workflow definitions loaded from XAML (XamlServices.Load() returns an ActivityBuilder object, not an Activity), but it does not change the requirement that changes be applied to the in-memory workflow definition.

The Code Approach

Let’s focus now on step A.2 above, the in-memory changes to the workflow definition.

The easiest approach to the in-memory changes is to hard-code the changes in code. This link exemplifies this process. See, specifically, the “To update StateMachineNumberGuessWorkflow”, “To update FlowchartNumberGuessWorkflow” and | To update SequentialNumberGuessWorkflow” sections.

This approach works just fine. However, by hard-coding the changes into the program, it

  • requires a different program for each new change
  • requires a programmer to make the change, instead of a business owner
  • may require a complex and hard-to-read sequence of statements in order to make a simple change to the workflow definition. This is especially true when the changes are made deep into the hierarchy of activities

In order to overcome these limitations, the changes would have to be made through the Workflow Designer, instead of being hard-coded. The requirement that the changes be made to the in-memory workflow definition rules out the Workflow Designer used in Visual Studio however: the WF Designer used in Visual Studio loads a workflow from a XAML file and saves the change to a XAML file, so it is not usable to make in-memory changes to a workflow definition.

The Re-hosted Workflow Designer Approach

The Workflow Designer, thankfully, is a reusable component that can be hosted in any process. Visual Studio just happens to be one host of Workflow Designer. By re-hosting the Workflow Designer in a custom host, we may be able to allow in-memory changes to the workflow definition through the Workflow Designer.

The Sample Code

The sample code attached to this post is general-purpose: the input is a workflow in XAML and the output, after the user makes changes to the workflow definition in the designer, are:

  • the XAML file of the modified workflow
  • the update map (XML file)

To use the sample:

  • Load a workflow from the FIle…Open Workflow Definition menu item. DynamicUpdateServices.PrepareForUpdate() is called at this stage.
  • Make the changes to the workflow definition in the designer. This includes adding activities, modifying existing activities, adding/removing variables and arguments.
  • Save the workflow and create the update map using the File…Save and Create Update Map menu item. First, you can save the workflow definition to a .XAML file. Then, if you choose to save the update map, DynamicUpdateServices.CreateUpdateMap() is called and the update map is saved to an XML file.

This is how the program looks like, when a workflow definition is loaded:

AppSnapshot

A word of caution

A re-hosted Workflow Designer does not always offer the same user experience of the Workflow Designer in Visual Studio, because the Workflow Designer can be configured in many different ways.

For instance, the Services and Items of the Editing Context may be different, or configured differently. Also, a re-hosted Workflow Designer has some limitations. As far as dynamic update of workflows goes, though, these limitations should not be a roadblock.

You’ll also notice that the behavior of the Toolbox in the sample differs from that of Visual Studio:

  • at startup, a pre-defined set of common activities is loaded in the Toolbox, grouped into a set of categories
  • when a workflow definition is loaded, the activities of the workflow are added to a new category called Auto, displayed at the top of the toolbox
  • it is possible to load additional activities by using the File | Add Activities in Assembly… menu item. All the activities in that assembly are added to a new category, named after the assembly, displayed at the bottom of the toolbox. You may want to load the System.Activities.dll assembly to see a broad range of activities (more than the Visual Studio toolbox shows)
  • you can remove an entire category or an activity from the toolbox by right-clicking on them and selecting Remove

The sample provided here is not production-ready code: error handling is minimal, it does not have tracing or logging, and it has not been extensively tested.

All this said :-), if you find any issue with the sample, please let me know.

Enjoy!

UpdateWithDesigner.zip


Comments (12)

  1. Vincent says:

    Hi Carlo,

    I'm trying to download your project but the file is 0 kb.

    Can you provide me with a new link please? I have the feeling you have the solution to my problems updating workflows 🙂

    Thanks you,

    Regards,

    Vincent

  2. Hi Vincent. Thanks for pointing this out.

    I uploaded the sample again. It should not be empty this time.

  3. Vincent says:

    Hi Carlo,

    Your idea of updating workflows by design is very good, yet i'm looking for a solution to load a workflow design version, let's say v1, and creating an update map with another version, v2, so I have the freedom to develop and test my new workflow design before calling PrepareForUpdate and CreateUpdateMap. I other words, I want to update long-running-processes with a new workflow design. Your idea is very good, but not suitable for my desired environment.

    You have any idea how I can fix this?

    Regards,

    Vincent

  4. mohamed mostafa says:

    I want to know how to apply the update map and the updated workflow definition in my code

  5. @Mohamed: the update can be applied by loading the workflow instance (WorkflowApplication.Load()), providing the update map as an argument.

    See msdn.microsoft.com/.../jj205427(v=vs.110).aspx, in the section "To apply the dynamic updates" and, more specifically, its step 19.

  6. Soumyadev Bhattacharyya says:

    Hi,

    I need help in understanding and solving dynamic updates for .Net 4.5 workflows

    I'm writing a console application to create a update map and then the idea is to update the persisted InstanceId of workflows using the map.

    Steps that I have done,

    • Create a .Net 4.5 Activity simple workflow

    • Persist the workflow using code to DB.

    • Saved the existing XAML file and .dll into a folder named as previous version.

    • Prepared the existing XAML file for update

    • Using console app, created a .map file from the XAML after adding a writeline activity

    • Saved the updated definition of XAML into a folder location

    • Trying to update the persisted workflow instance using the map. This when I’m getting an error

    “The update map does not match the workflow definition. Please make sure that this is the correct map for this definition, and that the serialized map has not been manually altered.”

    Code used to update the persisted instance with the map

    private static void updateWFInstanceWithMap(Guid CurrentInstance,

               SqlWorkflowInstanceStore CurrentPersistenceStore)

           {

               string path = Path.Combine(mapPath, "SequentialActivtiyflow.map");

               using (FileStream fs = File.Open(path, FileMode.Open))

               {

                   DataContractSerializer serializer = new DataContractSerializer(typeof(DynamicUpdateMap));

                   object updateMap = serializer.ReadObject(fs);

                   if (updateMap == null)

                   {

                       throw new ApplicationException("DynamicUpdateMap is null.");

                   }

                   map = (DynamicUpdateMap)updateMap;

               }

               // Create a workflow application. You must specify the original workflow definition, but

               // you may provide an updated WorkflowIdentity if desired to reflect the update.

               WorkflowIdentity identity = new WorkflowIdentity

               {

                   Name = "Activity1 V1",

                   Version = new Version(1, 2, 0, 0)

               };

               // Load the persisted workflow instance using the original workflow definition

               // but with an updated WorkflowIdentity.

               WorkflowApplication wfApplication = new WorkflowApplication(new Activity1(), identity);

               //WorkflowApplication wfApplication = new WorkflowApplication(new Activity1());

               // WorkflowApplication wfApplication = new WorkflowApplication(wf);

               WorkflowApplicationInstance wfInstance =

                   WorkflowApplication.GetInstance(CurrentInstance, CurrentPersistenceStore);

               wfApplication.Load(wfInstance, map);

               wfApplication.Run();

               Console.Read();

           }

  7. rahul says:

    Hello,

    When upgrading the instances, I am getting this error. Can you please help?

    'StateMachine' is not of type 'DynamicActivity'. When loading this instance you must ensure that the activity with name 'StateMachine' implements 'DynamicActivity'.

  8. @rahul: if you can share with me your instances, and the detailed steps of how to reproduce the behavior that you described, I will have a quick look.

  9. rahul says:

    Carlo, thank you for reply. My current code depends on a lot of DLLs, I will create a cut down version and share the DB + Code.

  10. andy_crc says:

    Hi, thanks for your help. Sorry por write a year after from the last comment.

    I'm trying your tool, I have an State Machine Workflow, it has two Custom Activities. I added both from Assembly (even, I have modified CreateTbx for add it), the activities appear on the toolbox, but, when I try open my Workflow, the line "instance = da.Implementation();" in menuOpen_Click, throw an Exception: "cannot create unknown type ' clr-namespace" + "myActivity". I don't now what can I do in this point, when I need load the namespace? I added the reference to the project, added in "Using" too. Thanks if you can read this message. Regards!

  11. andy_crc says:

    Sorry, I have been solved. I edited the workflow xaml, then, in

    xmlns:local=”clr-namespace:MyActivity;", I added "assembly=MyProject”. And loaded perfect! Thanks!

  12. andy_crc says:

    The Only problem is after generate the XAML file.

    When I add it, the project is not compile.

    Say: "The XAML MSBuild task only processes files that contain an '{schemas.microsoft.com/.../xaml}Class' directive. Please refer to documentation for usage of ‘{schemas.microsoft.com/.../xaml}Class’."

    But declaration is equals to original XAML. The original is ok. I'll continue with the review.

Skip to main content