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
Open the WF45GettingStartedTutorial solution in Visual Studio 2012 if it is not open.
Press CTRL+SHIFT+B to build the solution.
Close the WF45GettingStartedTutorial solution.
Open Windows Explorer and navigate to the folder where the tutorial solution file and the project folders are located.
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.
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.
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.
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.
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
In Solution Explorer, under the NumberGuessWorkflowActivities project, double-click StateMachineNumberGuessWorkflow.xaml.
Double-click the Guess Incorrect transition on the state machine.
Update the
Text
of the left-mostWriteLine
in theIf
activity.Guess & " is too low."
Guess + " is too low."
Update the
Text
of the right-mostWriteLine
in theIf
activity.Guess & " is too high."
Guess + " is too high."
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.
Double-click the Guess Correct transition on the state machine.
Drag a WriteLine activity from the Primitives section of the Toolbox and drop it on the Drop Action activity here label of the transition.
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
In Solution Explorer, under the NumberGuessWorkflowActivities project, double-click FlowchartNumberGuessWorkflow.xaml.
Update the
Text
of the left-mostWriteLine
activity.Guess & " is too low."
Guess + " is too low."
Update the
Text
of the right-mostWriteLine
activity.Guess & " is too high."
Guess + " is too high."
Drag a WriteLine activity from the Primitives section of the Toolbox and drop it on the drop point of the
True
action of the topmostFlowDecision
. TheWriteLine
activity is added to the flowchart and linked to theTrue
action of theFlowDecision
.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
In Solution Explorer, under the NumberGuessWorkflowActivities project, double-click SequentialNumberGuessWorkflow.xaml.
Update the
Text
of the left-mostWriteLine
in theIf
activity.Guess & " is too low."
Guess + " is too low."
Update the
Text
of the right-mostWriteLine
activity in theIf
activity.Guess & " is too high."
Guess + " is too high."
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.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
Double-click WorkflowVersionMap.cs (or WorkflowVersionMap.vb) under the NumberGuessWorkflowHost project to open it.
Add the following
using
(orImports
) statements to the top of the file with the otherusing
(orImports
) statements.Imports System.Reflection Imports System.IO
using System.Reflection; using System.IO;
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;
In the
WorkflowVersionMap
constructor, update theVersion
property of the three current workflow identities to2.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.
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.
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
Press CTRL+SHIFT+B to build the application, and then CTRL+F5 to start.
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 theInstanceId
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 theWriteLine
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 finalWriteLine
activity that was added in this topic is not. That is because the status window is updated by thePersistableIdle
handler. Because the workflow completes and does not go idle after the final activity, thePersistableIdle
handler is not called. However, a similar message is displayed in the status window by theCompleted
handler. If desired, code could be added to theCompleted
handler to extract the text from theStringWriter
and display it to the status window.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 theWriteLine
that was added in this topic.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 theWriteLine
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 thev2
instances.