How to: Host Multiple Versions of a Workflow Side-by-Side

WorkflowIdentity provides a way for workflow application developers to associate a name and a version with a workflow definition, and for this information to be associated with a persisted workflow instance. This identity information can be used by workflow application developers to enable scenarios such as side-by-side execution of multiple versions of a workflow definition, and provides the cornerstone for other functionality such as dynamic update. This step in the tutorial demonstrates how to use WorkflowIdentity to host multiple versions of a workflow at the same time.

In this topic

In this step of the tutorial, the WriteLine activities in the workflow are modified to provide additional information, and a new WriteLine activity is added. A copy of the original workflow assembly is stored, and the host application is updated so that it can run both the original and the updated workflows at the same time.

Note

Before following the steps in this topic, run the application, start several workflows of each type, and making one or two guesses for each one. These persisted workflows are used in this step and the following step, How to: Update the Definition of a Running Workflow Instance.

To make a copy of the NumberGuessWorkflowActivities project

  1. Open the WF45GettingStartedTutorial solution in Visual Studio 2012 if it is not open.

  2. Press CTRL+SHIFT+B to build the solution.

  3. Close the WF45GettingStartedTutorial solution.

  4. Open Windows Explorer and navigate to the folder where the tutorial solution file and the project folders are located.

  5. Create a new folder named PreviousVersions in the same folder as NumberGuessWorkflowHost and NumberGuessWorkflowActivities. This folder is used to contain the assemblies that contain the different versions of the workflows used in the subsequent tutorial steps.

  6. Navigate to the NumberGuessWorkflowActivities\bin\debug folder (or bin\release depending on your project settings). Copy NumberGuessWorkflowActivities.dll and paste it into the PreviousVersions folder.

  7. Rename NumberGuessWorkflowActivities.dll in the PreviousVersions folder to NumberGuessWorkflowActivities_v1.dll.

    Note

    The steps in this topic demonstrate one way to manage the assemblies used to contain multiple versions of the workflows. Other methods such as strong naming the assemblies and registering them in the global assembly cache could also be used.

  8. Create a new folder named NumberGuessWorkflowActivities_du in the same folder as NumberGuessWorkflowHost, NumberGuessWorkflowActivities, and the newly added PreviousVersions folder, and copy all of the files and subfolders from the NumberGuessWorkflowActivities folder into the new NumberGuessWorkflowActivities_du folder. This backup copy of the project for the initial version of the activities is used in How to: Update the Definition of a Running Workflow Instance.

  9. Re-open the WF45GettingStartedTutorial solution in Visual Studio 2012.

To update the workflows

In this section, the workflow definitions are updated. The two WriteLine activities that give feedback on the user's guess are updated, and a new WriteLine activity is added that provides additional information about the game once the number is guessed.

To update the StateMachine workflow

  1. In Solution Explorer, under the NumberGuessWorkflowActivities project, double-click StateMachineNumberGuessWorkflow.xaml.

  2. Double-click the Guess Incorrect transition on the state machine.

  3. Update the Text of the left-most WriteLine in the If activity.

    Guess & " is too low."
    
    Guess + " is too low."
    
  4. Update the Text of the right-most WriteLine in the If activity.

    Guess & " is too high."
    
    Guess + " is too high."
    
  5. Return to the overall state machine view in the workflow designer by clicking StateMachine in the breadcrumb display at the top of the workflow designer.

  6. Double-click the Guess Correct transition on the state machine.

  7. Drag a WriteLine activity from the Primitives section of the Toolbox and drop it on the Drop Action activity here label of the transition.

  8. Type the following expression into the Text property box.

    Guess & " is correct. You guessed it in " & Turns & " turns."
    
    Guess + " is correct. You guessed it in " + Turns + " turns."
    

To update the Flowchart workflow

  1. In Solution Explorer, under the NumberGuessWorkflowActivities project, double-click FlowchartNumberGuessWorkflow.xaml.

  2. Update the Text of the left-most WriteLine activity.

    Guess & " is too low."
    
    Guess + " is too low."
    
  3. Update the Text of the right-most WriteLine activity.

    Guess & " is too high."
    
    Guess + " is too high."
    
  4. Drag a WriteLine activity from the Primitives section of the Toolbox and drop it on the drop point of the True action of the topmost FlowDecision. The WriteLine activity is added to the flowchart and linked to the True action of the FlowDecision.

  5. Type the following expression into the Text property box.

    Guess & " is correct. You guessed it in " & Turns & " turns."
    
    Guess + " is correct. You guessed it in " + Turns + " turns."
    

To update the Sequential workflow

  1. In Solution Explorer, under the NumberGuessWorkflowActivities project, double-click SequentialNumberGuessWorkflow.xaml.

  2. Update the Text of the left-most WriteLine in the If activity.

    Guess & " is too low."
    
    Guess + " is too low."
    
  3. Update the Text of the right-most WriteLine activity in the If activity.

    Guess & " is too high."
    
    Guess + " is too high."
    
  4. Drag a WriteLine activity from the Primitives section of the Toolbox and drop it after the DoWhile activity so that the WriteLine is the final activity in the root Sequence activity.

  5. Type the following expression into the Text property box.

    Guess & " is correct. You guessed it in " & Turns & " turns."
    
    Guess + " is correct. You guessed it in " + Turns + " turns."
    

To update WorkflowVersionMap to include the previous workflow versions

  1. Double-click WorkflowVersionMap.cs (or WorkflowVersionMap.vb) under the NumberGuessWorkflowHost project to open it.

  2. Add the following using (or Imports) statements to the top of the file with the other using (or Imports) statements.

    Imports System.Reflection
    Imports System.IO
    
    using System.Reflection;
    using System.IO;
    
  3. Add three new workflow identities just below the three existing workflow identity declarations. These new v1 workflow identities will be used provide the correct workflow definition to workflows started before the updates were made.

    'Current version identities.
    Public StateMachineNumberGuessIdentity As WorkflowIdentity
    Public FlowchartNumberGuessIdentity As WorkflowIdentity
    Public SequentialNumberGuessIdentity As WorkflowIdentity
    
    'v1 Identities.
    Public StateMachineNumberGuessIdentity_v1 As WorkflowIdentity
    Public FlowchartNumberGuessIdentity_v1 As WorkflowIdentity
    Public SequentialNumberGuessIdentity_v1 As WorkflowIdentity
    
    // Current version identities.
    static public WorkflowIdentity StateMachineNumberGuessIdentity;
    static public WorkflowIdentity FlowchartNumberGuessIdentity;
    static public WorkflowIdentity SequentialNumberGuessIdentity;
    
    // v1 identities.
    static public WorkflowIdentity StateMachineNumberGuessIdentity_v1;
    static public WorkflowIdentity FlowchartNumberGuessIdentity_v1;
    static public WorkflowIdentity SequentialNumberGuessIdentity_v1;
    
  4. In the WorkflowVersionMap constructor, update the Version property of the three current workflow identities to 2.0.0.0.

    'Add the current workflow version identities.
    StateMachineNumberGuessIdentity = New WorkflowIdentity With
    {
        .Name = "StateMachineNumberGuessWorkflow",
        .Version = New Version(2, 0, 0, 0)
    }
    
    FlowchartNumberGuessIdentity = New WorkflowIdentity With
    {
        .Name = "FlowchartNumberGuessWorkflow",
        .Version = New Version(2, 0, 0, 0)
    }
    
    SequentialNumberGuessIdentity = New WorkflowIdentity With
    {
        .Name = "SequentialNumberGuessWorkflow",
        .Version = New Version(2, 0, 0, 0)
    }
    
    map.Add(StateMachineNumberGuessIdentity, New StateMachineNumberGuessWorkflow())
    map.Add(FlowchartNumberGuessIdentity, New FlowchartNumberGuessWorkflow())
    map.Add(SequentialNumberGuessIdentity, New SequentialNumberGuessWorkflow())
    
    // Add the current workflow version identities.
    StateMachineNumberGuessIdentity = new WorkflowIdentity
    {
        Name = "StateMachineNumberGuessWorkflow",
        // Version = new Version(1, 0, 0, 0),
        Version = new Version(2, 0, 0, 0)
    };
    
    FlowchartNumberGuessIdentity = new WorkflowIdentity
    {
        Name = "FlowchartNumberGuessWorkflow",
        // Version = new Version(1, 0, 0, 0),
        Version = new Version(2, 0, 0, 0)
    };
    
    SequentialNumberGuessIdentity = new WorkflowIdentity
    {
        Name = "SequentialNumberGuessWorkflow",
        // Version = new Version(1, 0, 0, 0),
        Version = new Version(2, 0, 0, 0)
    };
    
    map.Add(StateMachineNumberGuessIdentity, new StateMachineNumberGuessWorkflow());
    map.Add(FlowchartNumberGuessIdentity, new FlowchartNumberGuessWorkflow());
    map.Add(SequentialNumberGuessIdentity, new SequentialNumberGuessWorkflow());
    

    The code in that adds the current versions of the workflows to the dictionary uses the current versions that are referenced in the project, so the code that initializes the workflow definitions does not need to be updated.

  5. Add the following code in the constructor just after the code that adds the current versions to the dictionary.

    'Initialize the previous workflow version identities.
    StateMachineNumberGuessIdentity_v1 = New WorkflowIdentity With
    {
        .Name = "StateMachineNumberGuessWorkflow",
        .Version = New Version(1, 0, 0, 0)
    }
    
    FlowchartNumberGuessIdentity_v1 = New WorkflowIdentity With
    {
        .Name = "FlowchartNumberGuessWorkflow",
        .Version = New Version(1, 0, 0, 0)
    }
    
    SequentialNumberGuessIdentity_v1 = New WorkflowIdentity With
    {
        .Name = "SequentialNumberGuessWorkflow",
        .Version = New Version(1, 0, 0, 0)
    }
    
    // Initialize the previous workflow version identities.
    StateMachineNumberGuessIdentity_v1 = new WorkflowIdentity
    {
        Name = "StateMachineNumberGuessWorkflow",
        Version = new Version(1, 0, 0, 0)
    };
    
    FlowchartNumberGuessIdentity_v1 = new WorkflowIdentity
    {
        Name = "FlowchartNumberGuessWorkflow",
        Version = new Version(1, 0, 0, 0)
    };
    
    SequentialNumberGuessIdentity_v1 = new WorkflowIdentity
    {
        Name = "SequentialNumberGuessWorkflow",
        Version = new Version(1, 0, 0, 0)
    };
    

    These workflow identities are associated with the initial versions of the corresponding workflow definitions.

  6. Next, load the assembly that contains the initial version of the workflow definitions, and create and add the corresponding workflow definitions to the dictionary.

    'Add the previous version workflow identities to the dictionary along with
    'the corresponding workflow definitions loaded from the v1 assembly.
    'Assembly.LoadFile requires an absolute path so convert this relative path
    'to an absolute path.
    Dim v1AssemblyPath As String = "..\..\..\PreviousVersions\NumberGuessWorkflowActivities_v1.dll"
    v1AssemblyPath = Path.GetFullPath(v1AssemblyPath)
    Dim v1Assembly As Assembly = Assembly.LoadFile(v1AssemblyPath)
    
    map.Add(StateMachineNumberGuessIdentity_v1,
        v1Assembly.CreateInstance("NumberGuessWorkflowActivities.StateMachineNumberGuessWorkflow"))
    
    map.Add(SequentialNumberGuessIdentity_v1,
        v1Assembly.CreateInstance("NumberGuessWorkflowActivities.SequentialNumberGuessWorkflow"))
    
    map.Add(FlowchartNumberGuessIdentity_v1,
        v1Assembly.CreateInstance("NumberGuessWorkflowActivities.FlowchartNumberGuessWorkflow"))
    
    // Add the previous version workflow identities to the dictionary along with
    // the corresponding workflow definitions loaded from the v1 assembly.
    // Assembly.LoadFile requires an absolute path so convert this relative path
    // to an absolute path.
    string v1AssemblyPath = @"..\..\..\PreviousVersions\NumberGuessWorkflowActivities_v1.dll";
    v1AssemblyPath = Path.GetFullPath(v1AssemblyPath);
    Assembly v1Assembly = Assembly.LoadFile(v1AssemblyPath);
    
    map.Add(StateMachineNumberGuessIdentity_v1,
        v1Assembly.CreateInstance("NumberGuessWorkflowActivities.StateMachineNumberGuessWorkflow") as Activity);
    
    map.Add(SequentialNumberGuessIdentity_v1,
        v1Assembly.CreateInstance("NumberGuessWorkflowActivities.SequentialNumberGuessWorkflow") as Activity);
    
    map.Add(FlowchartNumberGuessIdentity_v1,
        v1Assembly.CreateInstance("NumberGuessWorkflowActivities.FlowchartNumberGuessWorkflow") as Activity);
    

    The following example is the complete listing for the updated WorkflowVersionMap class.

    Public Module WorkflowVersionMap
        Dim map As Dictionary(Of WorkflowIdentity, Activity)
    
        'Current version identities.
        Public StateMachineNumberGuessIdentity As WorkflowIdentity
        Public FlowchartNumberGuessIdentity As WorkflowIdentity
        Public SequentialNumberGuessIdentity As WorkflowIdentity
    
        'v1 Identities.
        Public StateMachineNumberGuessIdentity_v1 As WorkflowIdentity
        Public FlowchartNumberGuessIdentity_v1 As WorkflowIdentity
        Public SequentialNumberGuessIdentity_v1 As WorkflowIdentity
    
        Sub New()
            map = New Dictionary(Of WorkflowIdentity, Activity)
    
            'Add the current workflow version identities.
            StateMachineNumberGuessIdentity = New WorkflowIdentity With
            {
                .Name = "StateMachineNumberGuessWorkflow",
                .Version = New Version(2, 0, 0, 0)
            }
    
            FlowchartNumberGuessIdentity = New WorkflowIdentity With
            {
                .Name = "FlowchartNumberGuessWorkflow",
                .Version = New Version(2, 0, 0, 0)
            }
    
            SequentialNumberGuessIdentity = New WorkflowIdentity With
            {
                .Name = "SequentialNumberGuessWorkflow",
                .Version = New Version(2, 0, 0, 0)
            }
    
            map.Add(StateMachineNumberGuessIdentity, New StateMachineNumberGuessWorkflow())
            map.Add(FlowchartNumberGuessIdentity, New FlowchartNumberGuessWorkflow())
            map.Add(SequentialNumberGuessIdentity, New SequentialNumberGuessWorkflow())
    
            'Initialize the previous workflow version identities.
            StateMachineNumberGuessIdentity_v1 = New WorkflowIdentity With
            {
                .Name = "StateMachineNumberGuessWorkflow",
                .Version = New Version(1, 0, 0, 0)
            }
    
            FlowchartNumberGuessIdentity_v1 = New WorkflowIdentity With
            {
                .Name = "FlowchartNumberGuessWorkflow",
                .Version = New Version(1, 0, 0, 0)
            }
    
            SequentialNumberGuessIdentity_v1 = New WorkflowIdentity With
            {
                .Name = "SequentialNumberGuessWorkflow",
                .Version = New Version(1, 0, 0, 0)
            }
    
            'Add the previous version workflow identities to the dictionary along with
            'the corresponding workflow definitions loaded from the v1 assembly.
            'Assembly.LoadFile requires an absolute path so convert this relative path
            'to an absolute path.
            Dim v1AssemblyPath As String = "..\..\..\PreviousVersions\NumberGuessWorkflowActivities_v1.dll"
            v1AssemblyPath = Path.GetFullPath(v1AssemblyPath)
            Dim v1Assembly As Assembly = Assembly.LoadFile(v1AssemblyPath)
    
            map.Add(StateMachineNumberGuessIdentity_v1,
                v1Assembly.CreateInstance("NumberGuessWorkflowActivities.StateMachineNumberGuessWorkflow"))
    
            map.Add(SequentialNumberGuessIdentity_v1,
                v1Assembly.CreateInstance("NumberGuessWorkflowActivities.SequentialNumberGuessWorkflow"))
    
            map.Add(FlowchartNumberGuessIdentity_v1,
                v1Assembly.CreateInstance("NumberGuessWorkflowActivities.FlowchartNumberGuessWorkflow"))
        End Sub
    
        Public Function GetWorkflowDefinition(identity As WorkflowIdentity) As Activity
            Return map(identity)
        End Function
    
        Public Function GetIdentityDescription(identity As WorkflowIdentity) As String
            Return identity.ToString()
        End Function
    End Module
    
    public static class WorkflowVersionMap
    {
        static Dictionary<WorkflowIdentity, Activity> map;
    
        // Current version identities.
        static public WorkflowIdentity StateMachineNumberGuessIdentity;
        static public WorkflowIdentity FlowchartNumberGuessIdentity;
        static public WorkflowIdentity SequentialNumberGuessIdentity;
    
        // v1 identities.
        static public WorkflowIdentity StateMachineNumberGuessIdentity_v1;
        static public WorkflowIdentity FlowchartNumberGuessIdentity_v1;
        static public WorkflowIdentity SequentialNumberGuessIdentity_v1;
    
        static WorkflowVersionMap()
        {
            map = new Dictionary<WorkflowIdentity, Activity>();
    
            // Add the current workflow version identities.
            StateMachineNumberGuessIdentity = new WorkflowIdentity
            {
                Name = "StateMachineNumberGuessWorkflow",
                // Version = new Version(1, 0, 0, 0),
                Version = new Version(2, 0, 0, 0)
            };
    
            FlowchartNumberGuessIdentity = new WorkflowIdentity
            {
                Name = "FlowchartNumberGuessWorkflow",
                // Version = new Version(1, 0, 0, 0),
                Version = new Version(2, 0, 0, 0)
            };
    
            SequentialNumberGuessIdentity = new WorkflowIdentity
            {
                Name = "SequentialNumberGuessWorkflow",
                // Version = new Version(1, 0, 0, 0),
                Version = new Version(2, 0, 0, 0)
            };
    
            map.Add(StateMachineNumberGuessIdentity, new StateMachineNumberGuessWorkflow());
            map.Add(FlowchartNumberGuessIdentity, new FlowchartNumberGuessWorkflow());
            map.Add(SequentialNumberGuessIdentity, new SequentialNumberGuessWorkflow());
    
            // Initialize the previous workflow version identities.
            StateMachineNumberGuessIdentity_v1 = new WorkflowIdentity
            {
                Name = "StateMachineNumberGuessWorkflow",
                Version = new Version(1, 0, 0, 0)
            };
    
            FlowchartNumberGuessIdentity_v1 = new WorkflowIdentity
            {
                Name = "FlowchartNumberGuessWorkflow",
                Version = new Version(1, 0, 0, 0)
            };
    
            SequentialNumberGuessIdentity_v1 = new WorkflowIdentity
            {
                Name = "SequentialNumberGuessWorkflow",
                Version = new Version(1, 0, 0, 0)
            };
    
            // Add the previous version workflow identities to the dictionary along with
            // the corresponding workflow definitions loaded from the v1 assembly.
            // Assembly.LoadFile requires an absolute path so convert this relative path
            // to an absolute path.
            string v1AssemblyPath = @"..\..\..\PreviousVersions\NumberGuessWorkflowActivities_v1.dll";
            v1AssemblyPath = Path.GetFullPath(v1AssemblyPath);
            Assembly v1Assembly = Assembly.LoadFile(v1AssemblyPath);
    
            map.Add(StateMachineNumberGuessIdentity_v1,
                v1Assembly.CreateInstance("NumberGuessWorkflowActivities.StateMachineNumberGuessWorkflow") as Activity);
    
            map.Add(SequentialNumberGuessIdentity_v1,
                v1Assembly.CreateInstance("NumberGuessWorkflowActivities.SequentialNumberGuessWorkflow") as Activity);
    
            map.Add(FlowchartNumberGuessIdentity_v1,
                v1Assembly.CreateInstance("NumberGuessWorkflowActivities.FlowchartNumberGuessWorkflow") as Activity);
        }
    
        public static Activity GetWorkflowDefinition(WorkflowIdentity identity)
        {
            return map[identity];
        }
    
        public static string GetIdentityDescription(WorkflowIdentity identity)
        {
            return identity.ToString();
        }
    }
    

To build and run the application

  1. Press CTRL+SHIFT+B to build the application, and then CTRL+F5 to start.

  2. Start a new workflow by clicking New Game. The version of the workflow is displayed under the status window and reflects the updated version from the associated WorkflowIdentity. Make a note of the InstanceId so you can view the tracking file for the workflow when it completes, and then enter guesses until the game is complete. Note how the user's guess is displayed in the information displayed in the status window based on the updates to the WriteLine activities.

    Please enter a number between 1 and 10
    5 is too high.
    Please enter a number between 1 and 10
    3 is too high.
    Please enter a number between 1 and 10
    1 is too low.
    Please enter a number between 1 and 10
    Congratulations, you guessed the number in 4 turns.
    

    Note

    The updated text from the WriteLine activities is displayed, but the output of the final WriteLine activity that was added in this topic is not. That is because the status window is updated by the PersistableIdle handler. Because the workflow completes and does not go idle after the final activity, the PersistableIdle handler is not called. However, a similar message is displayed in the status window by the Completed handler. If desired, code could be added to the Completed handler to extract the text from the StringWriter and display it to the status window.

  3. Open Windows Explorer and navigate to the NumberGuessWorkflowHost\bin\debug folder (or bin\release depending on your project settings) and open the tracking file using Notepad that corresponds to the completed workflow. If you did not make a note of the InstanceId, you can identify the correct tracking file by using the Date modified information in Windows Explorer.

    Please enter a number between 1 and 10
    5 is too high.
    Please enter a number between 1 and 10
    3 is too high.
    Please enter a number between 1 and 10
    1 is too low.
    Please enter a number between 1 and 10
    2 is correct. You guessed it in 4 turns.
    

    The updated WriteLine output is contained within the tracking file, including the output of the WriteLine that was added in this topic.

  4. Switch back to the number guessing application and select one of the workflows that was started before the updates were made. You can identify the version of the currently selected workflow by looking at the version information that is displayed below the status window. Enter some guesses and note that the status updates match the WriteLine activity output from the previous version, and do not include the user's guess. That is because these workflows are using the previous workflow definition that does not have the WriteLine updates.

    In the next step, How to: Update the Definition of a Running Workflow Instance, the running v1 workflow instances are updated so they contain the new functionality as the v2 instances.