Training
Learning path
Solution Architect: Design Microsoft Power Platform solutions - Training
Learn how a solution architect designs solutions.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Dynamic update provides a mechanism for workflow application developers to update the workflow definition of a persisted workflow instance. The required change can be to implement a bug fix, new requirements, or to accommodate unexpected changes. This step in the tutorial demonstrates how to use dynamic update to modify persisted instances of the v1
number guessing workflow to match the new functionality introduced in How to: Host Multiple Versions of a Workflow Side-by-Side.
Right-click WF45GettingStartedTutorial in Solution Explorer and choose Add, New Project.
In the Installed node, select Visual C#, Windows (or Visual Basic, Windows).
Note
Depending on which programming language is configured as the primary language in Visual Studio, the Visual C# or Visual Basic node may be under the Other Languages node in the Installed node.
Ensure that .NET Framework 4.5 is selected in the .NET Framework version drop-down list. Select Console Application from the Windows list. Type CreateUpdateMaps into the Name box and click OK.
Right-click CreateUpdateMaps in Solution Explorer and choose Add Reference.
Select Framework from the Assemblies node in the Add Reference list. Type System.Activities into the Search Assemblies box to filter the assemblies and make the desired references easier to select.
Check the checkbox beside System.Activities from the Search Results list.
Type Serialization into the Search Assemblies box, and check the checkbox beside System.Runtime.Serialization from the Search Results list.
Type System.Xaml into the Search Assemblies box, and check the checkbox beside System.Xaml from the Search Results list.
Click OK to close Reference Manager and add the references.
Add the following using
(or Imports
) statements at the top of the file with the other using
(or Imports
) statements.
Imports System.Activities
Imports System.Activities.Statements
Imports System.Xaml
Imports System.Reflection
Imports System.IO
Imports System.Activities.XamlIntegration
Imports System.Activities.DynamicUpdate
Imports System.Runtime.Serialization
Imports Microsoft.VisualBasic.Activities
using System.Activities;
using System.Activities.Statements;
using System.IO;
using System.Xaml;
using System.Reflection;
using System.Activities.XamlIntegration;
using System.Activities.DynamicUpdate;
using System.Runtime.Serialization;
using Microsoft.CSharp.Activities;
Add the following two string members to the Program
class (or Module1
).
Const mapPath = "..\..\..\PreviousVersions"
Const definitionPath = "..\..\..\NumberGuessWorkflowActivities_du"
const string mapPath = @"..\..\..\PreviousVersions";
const string definitionPath = @"..\..\..\NumberGuessWorkflowActivities_du";
Add the following StartUpdate
method to the Program
class (or Module1
). This method loads up the specified xaml workflow definition into an ActivityBuilder
, and then calls DynamicUpdate.PrepareForUpdate
. PrepareForUpdate
makes a copy of the workflow definition inside the ActivityBuilder
. After the workflow definition is modified, this copy is used along with the modified workflow definition to create the update map.
Private Function StartUpdate(name As String) As ActivityBuilder
'Create the XamlXmlReaderSettings.
Dim readerSettings As XamlReaderSettings = New XamlXmlReaderSettings()
'In the XAML the "local" namespace refers to artifacts that come from
'the same project as the XAML. When loading XAML if the currently executing
'assembly is not the same assembly that was referred to as "local" in the XAML
'LocalAssembly must be set to the assembly containing the artifacts.
'Assembly.LoadFile requires an absolute path so convert this relative path
'to an absolute path.
readerSettings.LocalAssembly = Assembly.LoadFile(
Path.GetFullPath(Path.Combine(mapPath, "NumberGuessWorkflowActivities_v1.dll")))
Dim fullPath As String = Path.Combine(definitionPath, name)
Dim xamlReader As XamlXmlReader = New XamlXmlReader(fullPath, readerSettings)
'Load the workflow definition into an ActivityBuilder.
Dim wf As ActivityBuilder = XamlServices.Load(
ActivityXamlServices.CreateBuilderReader(xamlReader))
'PrepareForUpdate makes a copy of the workflow definition in the
'ActivityBuilder that is used for comparison when the update
'map is created.
DynamicUpdateServices.PrepareForUpdate(wf)
Return wf
End Function
private static ActivityBuilder StartUpdate(string name)
{
// Create the XamlXmlReaderSettings.
XamlXmlReaderSettings readerSettings = new XamlXmlReaderSettings()
{
// In the XAML the "local" namespace refers to artifacts that come from
// the same project as the XAML. When loading XAML if the currently executing
// assembly is not the same assembly that was referred to as "local" in the XAML
// LocalAssembly must be set to the assembly containing the artifacts.
// Assembly.LoadFile requires an absolute path so convert this relative path
// to an absolute path.
LocalAssembly = Assembly.LoadFile(
Path.GetFullPath(Path.Combine(mapPath, "NumberGuessWorkflowActivities_v1.dll")))
};
string path = Path.Combine(definitionPath, name);
XamlXmlReader xamlReader = new XamlXmlReader(path, readerSettings);
// Load the workflow definition into an ActivityBuilder.
ActivityBuilder wf = XamlServices.Load(
ActivityXamlServices.CreateBuilderReader(xamlReader))
as ActivityBuilder;
// PrepareForUpdate makes a copy of the workflow definition in the
// ActivityBuilder that is used for comparison when the update
// map is created.
DynamicUpdateServices.PrepareForUpdate(wf);
return wf;
}
Next, add the following CreateUpdateMethod
to the Program
class (or Module1
). This creates a dynamic update map by calling DynamicUpdateServices.CreateUpdateMap, and then saves the update map using the specified name. This update map contains the information needed by the workflow runtime to update a persisted workflow instance that was started using the original workflow definition contained in the ActivityBuilder
so that it completes using the updated workflow definition.
Private Sub CreateUpdateMaps(wf As ActivityBuilder, name As String)
'Create the UpdateMap.
Dim map As DynamicUpdateMap =
DynamicUpdateServices.CreateUpdateMap(wf)
'Serialize it to a file.
Dim mapFullPath As String = Path.Combine(mapPath, name)
Dim sz As DataContractSerializer = New DataContractSerializer(GetType(DynamicUpdateMap))
Using fs As FileStream = File.Open(mapFullPath, FileMode.Create)
sz.WriteObject(fs, map)
End Using
End Sub
private static void CreateUpdateMaps(ActivityBuilder wf, string name)
{
// Create the UpdateMap.
DynamicUpdateMap map =
DynamicUpdateServices.CreateUpdateMap(wf);
// Serialize it to a file.
string path = Path.Combine(mapPath, name);
DataContractSerializer sz = new DataContractSerializer(typeof(DynamicUpdateMap));
using (FileStream fs = System.IO.File.Open(path, FileMode.Create))
{
sz.WriteObject(fs, map);
}
}
Add the following SaveUpdatedDefinition
method to the Program
class (or Module1
). This method saves the updated workflow definition once the update map is created.
Private Sub SaveUpdatedDefinition(wf As ActivityBuilder, name As String)
Dim xamlPath As String = Path.Combine(definitionPath, name)
Dim sw As StreamWriter = File.CreateText(xamlPath)
Dim xw As XamlWriter = ActivityXamlServices.CreateBuilderWriter(
New XamlXmlWriter(sw, New XamlSchemaContext()))
XamlServices.Save(xw, wf)
sw.Close()
End Sub
private static void SaveUpdatedDefinition(ActivityBuilder wf, string name)
{
string xamlPath = Path.Combine(definitionPath, name);
StreamWriter sw = File.CreateText(xamlPath);
XamlWriter xw = ActivityXamlServices.CreateBuilderWriter(
new XamlXmlWriter(sw, new XamlSchemaContext()));
XamlServices.Save(xw, wf);
sw.Close();
}
Add a CreateStateMachineUpdateMap
to the Program
class (or Module1
).
Private Sub CreateStateMachineUpdateMap()
End Sub
private static void CreateStateMachineUpdateMap()
{
}
Make a call to StartUpdate
and then get a reference to the root StateMachine
activity of the workflow.
Dim wf As ActivityBuilder = StartUpdate("StateMachineNumberGuessWorkflow.xaml")
'Get a reference to the root StateMachine activity.
Dim sm As StateMachine = wf.Implementation
ActivityBuilder wf = StartUpdate("StateMachineNumberGuessWorkflow.xaml");
// Get a reference to the root StateMachine activity.
StateMachine sm = wf.Implementation as StateMachine;
Next, update the expressions of the two WriteLine
activities that display whether the user's guess is too high or too low so that they match the updates made in How to: Host Multiple Versions of a Workflow Side-by-Side.
'Update the Text of the two WriteLine activities that write the
'results of the user's guess. They are contained in the workflow as the
'Then and Else action of the If activity in sm.States[1].Transitions[1].Action.
Dim guessLow As Statements.If = sm.States(1).Transitions(1).Action
'Update the "too low" message.
Dim tooLow As WriteLine = guessLow.Then
tooLow.Text = New VisualBasicValue(Of String)("Guess.ToString() & "" is too low.""")
'Update the "too high" message.
Dim tooHigh As WriteLine = guessLow.Else
tooHigh.Text = New VisualBasicValue(Of String)("Guess.ToString() & "" is too high.""")
// Update the Text of the two WriteLine activities that write the
// results of the user's guess. They are contained in the workflow as the
// Then and Else action of the If activity in sm.States[1].Transitions[1].Action.
If guessLow = sm.States[1].Transitions[1].Action as If;
// Update the "too low" message.
WriteLine tooLow = guessLow.Then as WriteLine;
tooLow.Text = new CSharpValue<string>("Guess.ToString() + \" is too low.\"");
// Update the "too high" message.
WriteLine tooHigh = guessLow.Else as WriteLine;
tooHigh.Text = new CSharpValue<string>("Guess.ToString() + \" is too high.\"");
Next, add the new WriteLine
activity that displays the closing message.
'Create the new WriteLine that displays the closing message.
Dim wl As New WriteLine() With
{
.Text = New VisualBasicValue(Of String) _
("Guess.ToString() + "" is correct. You guessed it in "" & Turns.ToString() & "" turns.""")
}
'Add it as the Action for the Guess Correct transition. The Guess Correct
'transition is the first transition of States[1]. The transitions are listed
'at the bottom of the State activity designer.
sm.States(1).Transitions(0).Action = wl
// Create the new WriteLine that displays the closing message.
WriteLine wl = new WriteLine
{
Text = new CSharpValue<string>("Guess.ToString() + \" is correct. You guessed it in \" + Turns.ToString() + \" turns.\"")
};
// Add it as the Action for the Guess Correct transition. The Guess Correct
// transition is the first transition of States[1]. The transitions are listed
// at the bottom of the State activity designer.
sm.States[1].Transitions[0].Action = wl;
After the workflow is updated, call CreateUpdateMaps
and SaveUpdatedDefinition
. CreateUpdateMaps
creates and saves the DynamicUpdateMap
, and SaveUpdatedDefinition
saves the updated workflow definition.
'Create the update map.
CreateUpdateMaps(wf, "StateMachineNumberGuessWorkflow.map")
'Save the updated workflow definition.
SaveUpdatedDefinition(wf, "StateMachineNumberGuessWorkflow_du.xaml")
// Create the update map.
CreateUpdateMaps(wf, "StateMachineNumberGuessWorkflow.map");
// Save the updated workflow definition.
SaveUpdatedDefinition(wf, "StateMachineNumberGuessWorkflow_du.xaml");
The following example is the completed CreateStateMachineUpdateMap
method.
Private Sub CreateStateMachineUpdateMap()
Dim wf As ActivityBuilder = StartUpdate("StateMachineNumberGuessWorkflow.xaml")
'Get a reference to the root StateMachine activity.
Dim sm As StateMachine = wf.Implementation
'Update the Text of the two WriteLine activities that write the
'results of the user's guess. They are contained in the workflow as the
'Then and Else action of the If activity in sm.States[1].Transitions[1].Action.
Dim guessLow As Statements.If = sm.States(1).Transitions(1).Action
'Update the "too low" message.
Dim tooLow As WriteLine = guessLow.Then
tooLow.Text = New VisualBasicValue(Of String)("Guess.ToString() & "" is too low.""")
'Update the "too high" message.
Dim tooHigh As WriteLine = guessLow.Else
tooHigh.Text = New VisualBasicValue(Of String)("Guess.ToString() & "" is too high.""")
'Create the new WriteLine that displays the closing message.
Dim wl As New WriteLine() With
{
.Text = New VisualBasicValue(Of String) _
("Guess.ToString() + "" is correct. You guessed it in "" & Turns.ToString() & "" turns.""")
}
'Add it as the Action for the Guess Correct transition. The Guess Correct
'transition is the first transition of States[1]. The transitions are listed
'at the bottom of the State activity designer.
sm.States(1).Transitions(0).Action = wl
'Create the update map.
CreateUpdateMaps(wf, "StateMachineNumberGuessWorkflow.map")
'Save the updated workflow definition.
SaveUpdatedDefinition(wf, "StateMachineNumberGuessWorkflow_du.xaml")
End Sub
private static void CreateStateMachineUpdateMap()
{
ActivityBuilder wf = StartUpdate("StateMachineNumberGuessWorkflow.xaml");
// Get a reference to the root StateMachine activity.
StateMachine sm = wf.Implementation as StateMachine;
// Update the Text of the two WriteLine activities that write the
// results of the user's guess. They are contained in the workflow as the
// Then and Else action of the If activity in sm.States[1].Transitions[1].Action.
If guessLow = sm.States[1].Transitions[1].Action as If;
// Update the "too low" message.
WriteLine tooLow = guessLow.Then as WriteLine;
tooLow.Text = new CSharpValue<string>("Guess.ToString() + \" is too low.\"");
// Update the "too high" message.
WriteLine tooHigh = guessLow.Else as WriteLine;
tooHigh.Text = new CSharpValue<string>("Guess.ToString() + \" is too high.\"");
// Create the new WriteLine that displays the closing message.
WriteLine wl = new WriteLine
{
Text = new CSharpValue<string>("Guess.ToString() + \" is correct. You guessed it in \" + Turns.ToString() + \" turns.\"")
};
// Add it as the Action for the Guess Correct transition. The Guess Correct
// transition is the first transition of States[1]. The transitions are listed
// at the bottom of the State activity designer.
sm.States[1].Transitions[0].Action = wl;
// Create the update map.
CreateUpdateMaps(wf, "StateMachineNumberGuessWorkflow.map");
// Save the updated workflow definition.
SaveUpdatedDefinition(wf, "StateMachineNumberGuessWorkflow_du.xaml");
}
Add the following CreateFlowchartUpdateMethod
to the Program
class (or Module1
). This method is similar to CreateStateMachineUpdateMap
. It starts with a call to StartUpdate
, updates the flowchart workflow definition, and finishes by saving the update map and the updated workflow definition.
Private Sub CreateFlowchartUpdateMap()
Dim wf As ActivityBuilder = StartUpdate("FlowchartNumberGuessWorkflow.xaml")
'Get a reference to the root Flowchart activity.
Dim fc As Flowchart = wf.Implementation
'Update the Text of the two WriteLine activities that write the
'results of the user's guess. They are contained in the workflow as the
'True and False action of the "Guess < Target" FlowDecision, which is
'Nodes[4].
Dim guessLow As FlowDecision = fc.Nodes(4)
'Update the "too low" message.
Dim trueStep As FlowStep = guessLow.True
Dim tooLow As WriteLine = trueStep.Action
tooLow.Text = New VisualBasicValue(Of String)("Guess.ToString() & "" is too low.""")
'Update the "too high" message.
Dim falseStep As FlowStep = guessLow.False
Dim tooHigh As WriteLine = falseStep.Action
tooHigh.Text = New VisualBasicValue(Of String)("Guess.ToString() & "" is too high.""")
'Create the new WriteLine that displays the closing message.
Dim wl As New WriteLine() With
{
.Text = New VisualBasicValue(Of String) _
("Guess.ToString() + "" is correct. You guessed it in "" & Turns.ToString() & "" turns.""")
}
'Create a FlowStep to hold the WriteLine.
Dim closingStep As New FlowStep() With
{
.Action = wl
}
'Add this new FlowStep to the True action of the
'"Guess = Guess" FlowDecision
Dim guessCorrect As FlowDecision = fc.Nodes(3)
guessCorrect.True = closingStep
'Add the new FlowStep to the Nodes collection.
'If closingStep was replacing an existing node then
'we would need to remove that Step from the collection.
'In this example there was no existing True step to remove.
fc.Nodes.Add(closingStep)
'Create the update map.
CreateUpdateMaps(wf, "FlowchartNumberGuessWorkflow.map")
'Save the updated workflow definition.
SaveUpdatedDefinition(wf, "FlowchartNumberGuessWorkflow_du.xaml")
End Sub
private static void CreateFlowchartUpdateMap()
{
ActivityBuilder wf = StartUpdate("FlowchartNumberGuessWorkflow.xaml");
// Get a reference to the root Flowchart activity.
Flowchart fc = wf.Implementation as Flowchart;
// Update the Text of the two WriteLine activities that write the
// results of the user's guess. They are contained in the workflow as the
// True and False action of the "Guess < Target" FlowDecision, which is
// Nodes[4].
FlowDecision guessLow = fc.Nodes[4] as FlowDecision;
// Update the "too low" message.
FlowStep trueStep = guessLow.True as FlowStep;
WriteLine tooLow = trueStep.Action as WriteLine;
tooLow.Text = new CSharpValue<string>("Guess.ToString() + \" is too low.\"");
// Update the "too high" message.
FlowStep falseStep = guessLow.False as FlowStep;
WriteLine tooHigh = falseStep.Action as WriteLine;
tooHigh.Text = new CSharpValue<string>("Guess.ToString() + \" is too high.\"");
// Add the new WriteLine that displays the closing message.
WriteLine wl = new WriteLine
{
Text = new CSharpValue<string>("Guess.ToString() + \" is correct. You guessed it in \" + Turns.ToString() + \" turns.\"")
};
// Create a FlowStep to hold the WriteLine.
FlowStep closingStep = new FlowStep
{
Action = wl
};
// Add this new FlowStep to the True action of the
// "Guess == Guess" FlowDecision
FlowDecision guessCorrect = fc.Nodes[3] as FlowDecision;
guessCorrect.True = closingStep;
// Add the new FlowStep to the Nodes collection.
// If closingStep was replacing an existing node then
// we would need to remove that Step from the collection.
// In this example there was no existing True step to remove.
fc.Nodes.Add(closingStep);
// Create the update map.
CreateUpdateMaps(wf, "FlowchartNumberGuessWorkflow.map");
// Save the updated workflow definition.
SaveUpdatedDefinition(wf, "FlowchartNumberGuessWorkflow_du.xaml");
}
Add the following CreateSequentialUpdateMethod
to the Program
class (or Module1
). This method is similar to the other two methods. It starts with a call to StartUpdate
, updates the sequential workflow definition, and finishes by saving the update map and the updated workflow definition.
Private Sub CreateSequentialUpdateMap()
Dim wf As ActivityBuilder = StartUpdate("SequentialNumberGuessWorkflow.xaml")
'Get a reference to the root activity in the workflow.
Dim rootSequence As Sequence = wf.Implementation
'Update the Text of the two WriteLine activities that write the
'results of the user's guess. They are contained in the workflow as the
'Then and Else action of the "Guess < Target" If activity.
'Sequence[1]->DoWhile->Body->Sequence[2]->If->Then->If
Dim gameLoop As Statements.DoWhile = rootSequence.Activities(1)
Dim gameBody As Sequence = gameLoop.Body
Dim guessCorrect As Statements.If = gameBody.Activities(2)
Dim guessLow As Statements.If = guessCorrect.Then
Dim tooLow As WriteLine = guessLow.Then
tooLow.Text = New VisualBasicValue(Of String)("Guess.ToString() & "" is too low.""")
Dim tooHigh As WriteLine = guessLow.Else
tooHigh.Text = New VisualBasicValue(Of String)("Guess.ToString() & "" is too high.""")
'Create the new WriteLine that displays the closing message.
Dim wl As New WriteLine() With
{
.Text = New VisualBasicValue(Of String) _
("Guess.ToString() + "" is correct. You guessed it in "" & Turns.ToString() & "" turns.""")
}
'Insert it as the third activity in the root sequence
rootSequence.Activities.Insert(2, wl)
'Create the update map.
CreateUpdateMaps(wf, "SequentialNumberGuessWorkflow.map")
'Save the updated workflow definition.
SaveUpdatedDefinition(wf, "SequentialNumberGuessWorkflow_du.xaml")
End Sub
private static void CreateSequentialUpdateMap()
{
ActivityBuilder wf = StartUpdate("SequentialNumberGuessWorkflow.xaml");
// Get a reference to the root activity in the workflow.
Sequence rootSequence = wf.Implementation as Sequence;
// Update the Text of the two WriteLine activities that write the
// results of the user's guess. They are contained in the workflow as the
// Then and Else action of the "Guess < Target" If activity.
// Sequence[1]->DoWhile->Body->Sequence[2]->If->Then->If
DoWhile gameLoop = rootSequence.Activities[1] as DoWhile;
Sequence gameBody = gameLoop.Body as Sequence;
If guessCorrect = gameBody.Activities[2] as If;
If guessLow = guessCorrect.Then as If;
WriteLine tooLow = guessLow.Then as WriteLine;
tooLow.Text = new CSharpValue<string>("Guess.ToString() + \" is too low.\"");
WriteLine tooHigh = guessLow.Else as WriteLine;
tooHigh.Text = new CSharpValue<string>("Guess.ToString() + \" is too high.\"");
// Add the new WriteLine that displays the closing message.
WriteLine wl = new WriteLine
{
Text = new CSharpValue<string>("Guess.ToString() + \" is correct. You guessed it in \" + Turns.ToString() + \" turns.\"")
};
// Insert it as the third activity in the root sequence
rootSequence.Activities.Insert(2, wl);
// Create the update map.
CreateUpdateMaps(wf, "SequentialNumberGuessWorkflow.map");
// Save the updated workflow definition.
SaveUpdatedDefinition(wf, "SequentialNumberGuessWorkflow_du.xaml");
}
Update the Main
method and add the following three method calls. These methods are added in the following sections. Each method updates the corresponding number guess workflow and creates a DynamicUpdateMap
that describes the updates.
Sub Main()
'Create the update maps for the changes needed to the v1 activities
'so they match the v2 activities.
CreateSequentialUpdateMap()
CreateFlowchartUpdateMap()
CreateStateMachineUpdateMap()
End Sub
static void Main(string[] args)
{
// Create the update maps for the changes needed to the v1 activities
// so they match the v2 activities.
CreateSequentialUpdateMap();
CreateFlowchartUpdateMap();
CreateStateMachineUpdateMap();
}
Right-click CreateUpdateMaps in Solution Explorer and choose Set as StartUp Project.
Press CTRL+SHIFT+B to build the solution, and then CTRL+F5 to run the CreateUpdateMaps
application.
Note
The CreateUpdateMaps
application does not display any status information while running, but if you look in the NumberGuessWorkflowActivities_du folder and the PreviousVersions folder you will see the updated workflow definition files and the update maps.
Once the update maps are created and the workflow definitions updated, the next step is to build an updated workflow assembly containing the updated definitions.
Open a second instance of Visual Studio 2012.
Choose Open, Project/Solution from the File menu.
Navigate to the NumberGuessWorkflowActivities_du folder you created in How to: Host Multiple Versions of a Workflow Side-by-Side, select NumberGuessWorkflowActivities.csproj (or vbproj), and click Open.
In Solution Explorer, right click SequentialNumberGuessWorkflow.xaml and choose Exclude From Project. Do the same thing for FlowchartNumberGuessWorkflow.xaml and StateMachineNumberGuessWorkflow.xaml. This step removes the previous versions of the workflow definitions from the project.
Choose Add Existing Item from the Project menu.
Navigate to the NumberGuessWorkflowActivities_du folder you created in How to: Host Multiple Versions of a Workflow Side-by-Side.
Choose XAML Files (*.xaml;*.xoml) from the Files of type drop-down list.
Select SequentialNumberGuessWorkflow_du.xaml, FlowchartNumberGuessWorkflow_du.xaml, and StateMachineNumberGuessWorkflow_du.xaml and click Add.
Note
CTRL+Click to select multiple items at a time.
This step adds the updated versions of the workflow definitions to the project.
Press CTRL+SHIFT+B to build the project.
Choose Close Solution from the File menu. A solution file for the project is not required, so click No to close Visual Studio without saving a solution file. Choose Exit from the File menu to close Visual Studio.
Open Windows Explorer and navigate to the NumberGuessWorkflowActivities_du\bin\Debug folder (or bin\Release depending on your project settings).
Rename NumberGuessWorkflowActivities.dll to NumberGuessWorkflowActivities_v15.dll, and copy it to the PreviousVersions folder you created in How to: Host Multiple Versions of a Workflow Side-by-Side.
Switch back to the initial instance of Visual Studio 2012.
Double-click WorkflowVersionMap.cs (or WorkflowVersionMap.vb) under the NumberGuessWorkflowHost project to open it.
Add three new workflow identities just below the six existing workflow identity declarations. In this tutorial, 1.5.0.0
is used as the WorkflowIdentity.Version
for the dynamic update identities. These new v15
workflow identities will be used provide the correct workflow definition for the dynamically updated persisted workflow instances.
'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
'v1.5 (Dynamic Update) identities.
Public StateMachineNumberGuessIdentity_v15 As WorkflowIdentity
Public FlowchartNumberGuessIdentity_v15 As WorkflowIdentity
Public SequentialNumberGuessIdentity_v15 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;
// v1.5 (Dynamic Update) identities.
static public WorkflowIdentity StateMachineNumberGuessIdentity_v15;
static public WorkflowIdentity FlowchartNumberGuessIdentity_v15;
static public WorkflowIdentity SequentialNumberGuessIdentity_v15;
Add the following code at the end of the constructor. This code initializes the dynamic update workflow identities, loads the corresponding workflow definitions, and adds them to the workflow version dictionary.
'Initialize the dynamic update workflow identities.
StateMachineNumberGuessIdentity_v15 = New WorkflowIdentity With
{
.Name = "StateMachineNumberGuessWorkflow",
.Version = New Version(1, 5, 0, 0)
}
FlowchartNumberGuessIdentity_v15 = New WorkflowIdentity With
{
.Name = "FlowchartNumberGuessWorkflow",
.Version = New Version(1, 5, 0, 0)
}
SequentialNumberGuessIdentity_v15 = New WorkflowIdentity With
{
.Name = "SequentialNumberGuessWorkflow",
.Version = New Version(1, 5, 0, 0)
}
'Add the dynamic update workflow identities to the dictionary along with
'the corresponding workflow definitions loaded from the v15 assembly.
'Assembly.LoadFile requires an absolute path so convert this relative path
'to an absolute path.
Dim v15AssemblyPath As String = "..\..\..\PreviousVersions\NumberGuessWorkflowActivities_v15.dll"
v15AssemblyPath = Path.GetFullPath(v15AssemblyPath)
Dim v15Assembly As Assembly = Assembly.LoadFile(v15AssemblyPath)
map.Add(StateMachineNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.StateMachineNumberGuessWorkflow"))
map.Add(SequentialNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.SequentialNumberGuessWorkflow"))
map.Add(FlowchartNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.FlowchartNumberGuessWorkflow"))
// Initialize the dynamic update workflow identities.
StateMachineNumberGuessIdentity_v15 = new WorkflowIdentity
{
Name = "StateMachineNumberGuessWorkflow",
Version = new Version(1, 5, 0, 0)
};
FlowchartNumberGuessIdentity_v15 = new WorkflowIdentity
{
Name = "FlowchartNumberGuessWorkflow",
Version = new Version(1, 5, 0, 0)
};
SequentialNumberGuessIdentity_v15 = new WorkflowIdentity
{
Name = "SequentialNumberGuessWorkflow",
Version = new Version(1, 5, 0, 0)
};
// Add the dynamic update workflow identities to the dictionary along with
// the corresponding workflow definitions loaded from the v15 assembly.
// Assembly.LoadFile requires an absolute path so convert this relative path
// to an absolute path.
string v15AssemblyPath = @"..\..\..\PreviousVersions\NumberGuessWorkflowActivities_v15.dll";
v15AssemblyPath = Path.GetFullPath(v15AssemblyPath);
Assembly v15Assembly = Assembly.LoadFile(v15AssemblyPath);
map.Add(StateMachineNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.StateMachineNumberGuessWorkflow") as Activity);
map.Add(SequentialNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.SequentialNumberGuessWorkflow") as Activity);
map.Add(FlowchartNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.FlowchartNumberGuessWorkflow") as Activity);
The following example is the completed 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
'v1.5 (Dynamic Update) identities.
Public StateMachineNumberGuessIdentity_v15 As WorkflowIdentity
Public FlowchartNumberGuessIdentity_v15 As WorkflowIdentity
Public SequentialNumberGuessIdentity_v15 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"))
'Initialize the dynamic update workflow identities.
StateMachineNumberGuessIdentity_v15 = New WorkflowIdentity With
{
.Name = "StateMachineNumberGuessWorkflow",
.Version = New Version(1, 5, 0, 0)
}
FlowchartNumberGuessIdentity_v15 = New WorkflowIdentity With
{
.Name = "FlowchartNumberGuessWorkflow",
.Version = New Version(1, 5, 0, 0)
}
SequentialNumberGuessIdentity_v15 = New WorkflowIdentity With
{
.Name = "SequentialNumberGuessWorkflow",
.Version = New Version(1, 5, 0, 0)
}
'Add the dynamic update workflow identities to the dictionary along with
'the corresponding workflow definitions loaded from the v15 assembly.
'Assembly.LoadFile requires an absolute path so convert this relative path
'to an absolute path.
Dim v15AssemblyPath As String = "..\..\..\PreviousVersions\NumberGuessWorkflowActivities_v15.dll"
v15AssemblyPath = Path.GetFullPath(v15AssemblyPath)
Dim v15Assembly As Assembly = Assembly.LoadFile(v15AssemblyPath)
map.Add(StateMachineNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.StateMachineNumberGuessWorkflow"))
map.Add(SequentialNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.SequentialNumberGuessWorkflow"))
map.Add(FlowchartNumberGuessIdentity_v15,
v15Assembly.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;
// v1.5 (Dynamic Update) identities.
static public WorkflowIdentity StateMachineNumberGuessIdentity_v15;
static public WorkflowIdentity FlowchartNumberGuessIdentity_v15;
static public WorkflowIdentity SequentialNumberGuessIdentity_v15;
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);
// Initialize the dynamic update workflow identities.
StateMachineNumberGuessIdentity_v15 = new WorkflowIdentity
{
Name = "StateMachineNumberGuessWorkflow",
Version = new Version(1, 5, 0, 0)
};
FlowchartNumberGuessIdentity_v15 = new WorkflowIdentity
{
Name = "FlowchartNumberGuessWorkflow",
Version = new Version(1, 5, 0, 0)
};
SequentialNumberGuessIdentity_v15 = new WorkflowIdentity
{
Name = "SequentialNumberGuessWorkflow",
Version = new Version(1, 5, 0, 0)
};
// Add the dynamic update workflow identities to the dictionary along with
// the corresponding workflow definitions loaded from the v15 assembly.
// Assembly.LoadFile requires an absolute path so convert this relative path
// to an absolute path.
string v15AssemblyPath = @"..\..\..\PreviousVersions\NumberGuessWorkflowActivities_v15.dll";
v15AssemblyPath = Path.GetFullPath(v15AssemblyPath);
Assembly v15Assembly = Assembly.LoadFile(v15AssemblyPath);
map.Add(StateMachineNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.StateMachineNumberGuessWorkflow") as Activity);
map.Add(SequentialNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.SequentialNumberGuessWorkflow") as Activity);
map.Add(FlowchartNumberGuessIdentity_v15,
v15Assembly.CreateInstance("NumberGuessWorkflowActivities.FlowchartNumberGuessWorkflow") as Activity);
}
public static Activity GetWorkflowDefinition(WorkflowIdentity identity)
{
return map[identity];
}
public static string GetIdentityDescription(WorkflowIdentity identity)
{
return identity.ToString();
}
}
Press CTRL+SHIFT+B to build the project.
Right-click WF45GettingStartedTutorial in Solution Explorer and choose Add, New Project.
In the Installed node, select Visual C#, Windows (or Visual Basic, Windows).
Note
Depending on which programming language is configured as the primary language in Visual Studio, the Visual C# or Visual Basic node may be under the Other Languages node in the Installed node.
Ensure that .NET Framework 4.5 is selected in the .NET Framework version drop-down list. Select Console Application from the Windows list. Type ApplyDynamicUpdate into the Name box and click OK.
Right-click ApplyDynamicUpdate in Solution Explorer and choose Add Reference.
Click Solution and check the box next to NumberGuessWorkflowHost. This reference is needed so that ApplyDynamicUpdate
can use the NumberGuessWorkflowHost.WorkflowVersionMap
class.
Select Framework from the Assemblies node in the Add Reference list. Type System.Activities into the Search Assemblies box. This will filter the assemblies and make the desired references easier to select.
Check the checkbox beside System.Activities from the Search Results list.
Type Serialization into the Search Assemblies box, and check the checkbox beside System.Runtime.Serialization from the Search Results list.
Type DurableInstancing into the Search Assemblies box, and check the checkbox beside System.Activities.DurableInstancing and System.Runtime.DurableInstancing from the Search Results list.
Click OK to close Reference Manager and add the references.
Right-click ApplyDynamicUpdate in Solution Explorer and choose Add, Class. Type DynamicUpdateInfo
into the Name box and click Add.
Add the following two members to the DynamicUpdateInfo
class. The following example is the completed DynamicUpdateInfo
class. This class contains information on the update map and new workflow identity used when a workflow instance is updated.
Public Class DynamicUpdateInfo
Public updateMap As DynamicUpdateMap
Public newIdentity As WorkflowIdentity
End Class
class DynamicUpdateInfo
{
public DynamicUpdateMap updateMap;
public WorkflowIdentity newIdentity;
}
Add the following using
(or Imports
) statements at the top of the file with the other using
(or Imports
) statements.
Imports System.Activities
Imports System.Activities.DynamicUpdate
using System.Activities;
using System.Activities.DynamicUpdate;
Double-click Program.cs (or Module1.vb) in Solution Explorer.
Add the following using
(or Imports
) statements at the top of the file with the other using
(or Imports
) statements.
Imports NumberGuessWorkflowHost
Imports System.Data.SqlClient
Imports System.Activities.DynamicUpdate
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Activities
Imports System.Activities.DurableInstancing
using NumberGuessWorkflowHost;
using System.Data;
using System.Data.SqlClient;
using System.Activities;
using System.Activities.DynamicUpdate;
using System.IO;
using System.Runtime.Serialization;
using System.Activities.DurableInstancing;
Add the following connection string member to the Program
class (or Module1
).
Const connectionString = "Server=.\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI"
const string connectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI";
Important
Microsoft recommends that you use the most secure authentication flow available. If you're connecting to Azure SQL, Managed Identities for Azure resources is the recommended authentication method.
Note
Depending on your edition of SQL Server, the connection string server name might be different.
Add the following GetIDs
method to the Program
class (or Module1
). This method returns a list of persisted workflow instance ids.
Function GetIds() As IList(Of Guid)
Dim Ids As New List(Of Guid)
Dim localCmd = _
String.Format("Select [InstanceId] from [System.Activities.DurableInstancing].[Instances] Order By [CreationTime]")
Using localCon = New SqlConnection(connectionString)
Dim cmd As SqlCommand = localCon.CreateCommand()
cmd.CommandText = localCmd
localCon.Open()
Using reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While reader.Read()
'Get the InstanceId of the persisted Workflow
Dim id As Guid = Guid.Parse(reader(0).ToString())
'Add it to the list.
Ids.Add(id)
End While
End Using
End Using
Return Ids
End Function
static IList<Guid> GetIds()
{
List<Guid> Ids = new List<Guid>();
string localCmd = string.Format("Select [InstanceId] from [System.Activities.DurableInstancing].[Instances] Order By [CreationTime]");
using (SqlConnection localCon = new SqlConnection(connectionString))
{
SqlCommand cmd = localCon.CreateCommand();
cmd.CommandText = localCmd;
localCon.Open();
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
// Get the InstanceId of the persisted Workflow
Guid id = Guid.Parse(reader[0].ToString());
// Add it to the list.
Ids.Add(id);
}
}
}
return Ids;
}
Add the following LoadMap
method to the Program
class (or Module1
). This method creates a dictionary that maps v1
workflow identities to the update maps and new workflow identities used to update the corresponding persisted workflow instances.
Function LoadMap(mapName As String) As DynamicUpdateMap
Dim mapPath As String = Path.Combine("..\..\..\PreviousVersions", mapName)
Dim map As DynamicUpdateMap
Using fs As FileStream = File.Open(mapPath, FileMode.Open)
Dim serializer As DataContractSerializer = New DataContractSerializer(GetType(DynamicUpdateMap))
Dim updateMap = serializer.ReadObject(fs)
If updateMap Is Nothing Then
Throw New ApplicationException("DynamicUpdateMap is null.")
End If
map = updateMap
End Using
Return map
End Function
static DynamicUpdateMap LoadMap(string mapName)
{
string path = Path.Combine(@"..\..\..\PreviousVersions", mapName);
DynamicUpdateMap 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 = updateMap as DynamicUpdateMap;
}
return map;
}
Add the following LoadMaps
method to the Program
class (or Module1
). This method loads the three update maps and creates a dictionary that maps v1
workflow identities to the update maps.
Function LoadMaps() As IDictionary(Of WorkflowIdentity, DynamicUpdateInfo)
'There are 3 update maps to describe the changes to update v1 workflows,
'one for reach of the 3 workflow types in the tutorial.
Dim maps = New Dictionary(Of WorkflowIdentity, DynamicUpdateInfo)()
Dim sequentialMap As DynamicUpdateMap = LoadMap("SequentialNumberGuessWorkflow.map")
Dim sequentialInfo = New DynamicUpdateInfo With
{
.updateMap = sequentialMap,
.newIdentity = WorkflowVersionMap.SequentialNumberGuessIdentity_v15
}
maps.Add(WorkflowVersionMap.SequentialNumberGuessIdentity_v1, sequentialInfo)
Dim stateMap As DynamicUpdateMap = LoadMap("StateMachineNumberGuessWorkflow.map")
Dim stateInfo = New DynamicUpdateInfo With
{
.updateMap = stateMap,
.newIdentity = WorkflowVersionMap.StateMachineNumberGuessIdentity_v15
}
maps.Add(WorkflowVersionMap.StateMachineNumberGuessIdentity_v1, stateInfo)
Dim flowchartMap As DynamicUpdateMap = LoadMap("FlowchartNumberGuessWorkflow.map")
Dim flowchartInfo = New DynamicUpdateInfo With
{
.updateMap = flowchartMap,
.newIdentity = WorkflowVersionMap.FlowchartNumberGuessIdentity_v15
}
maps.Add(WorkflowVersionMap.FlowchartNumberGuessIdentity_v1, flowchartInfo)
Return maps
End Function
static IDictionary<WorkflowIdentity, DynamicUpdateInfo> LoadMaps()
{
// There are 3 update maps to describe the changes to update v1 workflows,
// one for reach of the 3 workflow types in the tutorial.
Dictionary<WorkflowIdentity, DynamicUpdateInfo> maps =
new Dictionary<WorkflowIdentity, DynamicUpdateInfo>();
DynamicUpdateMap sequentialMap = LoadMap("SequentialNumberGuessWorkflow.map");
DynamicUpdateInfo sequentialInfo = new DynamicUpdateInfo
{
updateMap = sequentialMap,
newIdentity = WorkflowVersionMap.SequentialNumberGuessIdentity_v15
};
maps.Add(WorkflowVersionMap.SequentialNumberGuessIdentity_v1, sequentialInfo);
DynamicUpdateMap stateMap = LoadMap("StateMachineNumberGuessWorkflow.map");
DynamicUpdateInfo stateInfo = new DynamicUpdateInfo
{
updateMap = stateMap,
newIdentity = WorkflowVersionMap.StateMachineNumberGuessIdentity_v15
};
maps.Add(WorkflowVersionMap.StateMachineNumberGuessIdentity_v1, stateInfo);
DynamicUpdateMap flowchartMap = LoadMap("FlowchartNumberGuessWorkflow.map");
DynamicUpdateInfo flowchartInfo = new DynamicUpdateInfo
{
updateMap = flowchartMap,
newIdentity = WorkflowVersionMap.FlowchartNumberGuessIdentity_v15
};
maps.Add(WorkflowVersionMap.FlowchartNumberGuessIdentity_v1, flowchartInfo);
return maps;
}
Add the following code to Main
. This code iterates the persisted workflow instances and examines each WorkflowIdentity
. If the WorkflowIdentity
maps to a v1
workflow instance, a WorkflowApplication
is configured with the updated workflow definition and an updated workflow identity. Next, WorkflowApplication.Load
is called with the instance and the update map, which applies the dynamic update map. Once the update is applied, the updated instance is persisted with a call to Unload
.
Dim store = New SqlWorkflowInstanceStore(connectionString)
WorkflowApplication.CreateDefaultInstanceOwner(store, Nothing, WorkflowIdentityFilter.Any)
Dim updateMaps As IDictionary(Of WorkflowIdentity, DynamicUpdateInfo) = LoadMaps()
For Each id As Guid In GetIds()
'Get a proxy to the instance.
Dim instance As WorkflowApplicationInstance = WorkflowApplication.GetInstance(id, store)
Console.WriteLine("Inspecting: {0}", instance.DefinitionIdentity)
'Only update v1 workflows.
If Not instance.DefinitionIdentity Is Nothing AndAlso _
instance.DefinitionIdentity.Version.Equals(New Version(1, 0, 0, 0)) Then
Dim info As DynamicUpdateInfo = updateMaps(instance.DefinitionIdentity)
'Associate the persisted WorkflowApplicationInstance with
'a WorkflowApplication that is configured with the updated
'definition and updated WorkflowIdentity.
Dim wf As Activity = WorkflowVersionMap.GetWorkflowDefinition(info.newIdentity)
Dim wfApp = New WorkflowApplication(wf, info.newIdentity)
'Apply the Dynamic Update.
wfApp.Load(instance, info.updateMap)
'Persist the updated instance.
wfApp.Unload()
Console.WriteLine("Updated to: {0}", info.newIdentity)
Else
'Not updating this instance, so unload it.
instance.Abandon()
End If
Next
SqlWorkflowInstanceStore store = new SqlWorkflowInstanceStore(connectionString);
WorkflowApplication.CreateDefaultInstanceOwner(store, null, WorkflowIdentityFilter.Any);
IDictionary<WorkflowIdentity, DynamicUpdateInfo> updateMaps = LoadMaps();
foreach (Guid id in GetIds())
{
// Get a proxy to the instance.
WorkflowApplicationInstance instance =
WorkflowApplication.GetInstance(id, store);
Console.WriteLine("Inspecting: {0}", instance.DefinitionIdentity);
// Only update v1 workflows.
if (instance.DefinitionIdentity != null &&
instance.DefinitionIdentity.Version.Equals(new Version(1, 0, 0, 0)))
{
DynamicUpdateInfo info = updateMaps[instance.DefinitionIdentity];
// Associate the persisted WorkflowApplicationInstance with
// a WorkflowApplication that is configured with the updated
// definition and updated WorkflowIdentity.
Activity wf = WorkflowVersionMap.GetWorkflowDefinition(info.newIdentity);
WorkflowApplication wfApp =
new WorkflowApplication(wf, info.newIdentity);
// Apply the Dynamic Update.
wfApp.Load(instance, info.updateMap);
// Persist the updated instance.
wfApp.Unload();
Console.WriteLine("Updated to: {0}", info.newIdentity);
}
else
{
// Not updating this instance, so unload it.
instance.Abandon();
}
}
Right-click ApplyDynamicUpdate in Solution Explorer and choose Set as StartUp Project.
Press Ctrl+Shift+B to build the solution, and then press Ctrl+F5 to run the ApplyDynamicUpdate
application and update the persisted workflow instances. You should see output similar to the following. The version 1.0.0.0 workflows are updated to version 1.5.0.0, while the version 2.0.0.0 workflows are not updated.
Inspecting: StateMachineNumberGuessWorkflow; Version=1.0.0.0
Updated to: StateMachineNumberGuessWorkflow; Version=1.5.0.0
Inspecting: StateMachineNumberGuessWorkflow; Version=1.0.0.0
Updated to: StateMachineNumberGuessWorkflow; Version=1.5.0.0
Inspecting: FlowchartNumberGuessWorkflow; Version=1.0.0.0
Updated to: FlowchartNumberGuessWorkflow; Version=1.5.0.0
Inspecting: FlowchartNumberGuessWorkflow; Version=1.0.0.0
Updated to: FlowchartNumberGuessWorkflow; Version=1.5.0.0
Inspecting: SequentialNumberGuessWorkflow; Version=1.0.0.0
Updated to: SequentialNumberGuessWorkflow; Version=1.5.0.0
Inspecting: SequentialNumberGuessWorkflow; Version=1.0.0.0
Updated to: SequentialNumberGuessWorkflow; Version=1.5.0.0
Inspecting: SequentialNumberGuessWorkflow; Version=1.0.0.0
Updated to: SequentialNumberGuessWorkflow; Version=1.5.0.0
Inspecting: StateMachineNumberGuessWorkflow; Version=1.0.0.0
Updated to: StateMachineNumberGuessWorkflow; Version=1.5.0.0
Inspecting: FlowchartNumberGuessWorkflow; Version=1.0.0.0
Updated to: FlowchartNumberGuessWorkflow; Version=1.5.0.0
Inspecting: StateMachineNumberGuessWorkflow; Version=2.0.0.0
Inspecting: StateMachineNumberGuessWorkflow; Version=2.0.0.0
Inspecting: FlowchartNumberGuessWorkflow; Version=2.0.0.0
Inspecting: FlowchartNumberGuessWorkflow; Version=2.0.0.0
Inspecting: SequentialNumberGuessWorkflow; Version=2.0.0.0
Inspecting: SequentialNumberGuessWorkflow; Version=2.0.0.0
Press any key to continue . . .
Right-click NumberGuessWorkflowHost in Solution Explorer and choose Set as StartUp Project.
Press CTRL+F5 to run the application.
Click New Game to start a new workflow and note the version information below the status window that indicates the workflow is a v2
workflow.
Select one of the v1
workflows you started at the beginning of the How to: Host Multiple Versions of a Workflow Side-by-Side topic. Note that the version information under the status window indicates that the workflow is a version 1.5.0.0 workflow. Note that there is no information indicated about previous guesses other than whether they were too high or too low.
Please enter a number between 1 and 10
Your guess is too low.
Make a note of the InstanceId
and then enter guesses until the workflow completes. The status window displays information about the content of the guess because the WriteLine
activities were updated by the dynamic update.
Please enter a number between 1 and 10
Your guess is too low.
Please enter a number between 1 and 10
5 is too low.
Please enter a number between 1 and 10
7 is too high.
Please enter a number between 1 and 10
Congratulations, you guessed the number in 4 turns.
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 may be able to identify the correct tracking file by using the Date modified information in Windows Explorer. The last line of the tracking information contains the output of the newly added WriteLine
activity.
Please enter a number between 1 and 10
Your guess is too low.
Please enter a number between 1 and 10
5 is too low.
Please enter a number between 1 and 10
7 is too high.
Please enter a number between 1 and 10
6 is correct. You guessed it in 4 turns.
If you run out of workflows to update, you can modify the NumberGuessWorkflowHost
application to enable starting previous versions of the workflows.
Double-click WorkflowHostForm in Solution Explorer, and select the WorkflowType combo box.
In the Properties window, select the Items property and click the ellipsis button to edit the Items collection.
Add the following three items to the collection.
StateMachineNumberGuessWorkflow v1
FlowchartNumberGuessWorkflow v1
SequentialNumberGuessWorkflow v1
The completed Items
collection will have six items.
StateMachineNumberGuessWorkflow
FlowchartNumberGuessWorkflow
SequentialNumberGuessWorkflow
StateMachineNumberGuessWorkflow v1
FlowchartNumberGuessWorkflow v1
SequentialNumberGuessWorkflow v1
Double-click WorkflowHostForm in Solution Explorer, and select View Code.
Add three new cases to the switch
(or Select Case
) statement in the NewGame_Click
handler to map the new items in the WorkflowType combo box to the matching workflow identities.
Case "SequentialNumberGuessWorkflow v1"
identity = WorkflowVersionMap.SequentialNumberGuessIdentity_v1
Case "StateMachineNumberGuessWorkflow v1"
identity = WorkflowVersionMap.StateMachineNumberGuessIdentity_v1
Case "FlowchartNumberGuessWorkflow v1"
identity = WorkflowVersionMap.FlowchartNumberGuessIdentity_v1
case "SequentialNumberGuessWorkflow v1":
identity = WorkflowVersionMap.SequentialNumberGuessIdentity_v1;
break;
case "StateMachineNumberGuessWorkflow v1":
identity = WorkflowVersionMap.StateMachineNumberGuessIdentity_v1;
break;
case "FlowchartNumberGuessWorkflow v1":
identity = WorkflowVersionMap.FlowchartNumberGuessIdentity_v1;
break;
The following example contains the complete switch
(or Select Case
) statement.
Select Case WorkflowType.SelectedItem.ToString()
Case "SequentialNumberGuessWorkflow"
identity = WorkflowVersionMap.SequentialNumberGuessIdentity
Case "StateMachineNumberGuessWorkflow"
identity = WorkflowVersionMap.StateMachineNumberGuessIdentity
Case "FlowchartNumberGuessWorkflow"
identity = WorkflowVersionMap.FlowchartNumberGuessIdentity
Case "SequentialNumberGuessWorkflow v1"
identity = WorkflowVersionMap.SequentialNumberGuessIdentity_v1
Case "StateMachineNumberGuessWorkflow v1"
identity = WorkflowVersionMap.StateMachineNumberGuessIdentity_v1
Case "FlowchartNumberGuessWorkflow v1"
identity = WorkflowVersionMap.FlowchartNumberGuessIdentity_v1
End Select
switch (WorkflowType.SelectedItem.ToString())
{
case "SequentialNumberGuessWorkflow":
identity = WorkflowVersionMap.SequentialNumberGuessIdentity;
break;
case "StateMachineNumberGuessWorkflow":
identity = WorkflowVersionMap.StateMachineNumberGuessIdentity;
break;
case "FlowchartNumberGuessWorkflow":
identity = WorkflowVersionMap.FlowchartNumberGuessIdentity;
break;
case "SequentialNumberGuessWorkflow v1":
identity = WorkflowVersionMap.SequentialNumberGuessIdentity_v1;
break;
case "StateMachineNumberGuessWorkflow v1":
identity = WorkflowVersionMap.StateMachineNumberGuessIdentity_v1;
break;
case "FlowchartNumberGuessWorkflow v1":
identity = WorkflowVersionMap.FlowchartNumberGuessIdentity_v1;
break;
};
Press CTRL+F5 to build and run the application. You can now start the v1
versions of the workflow as well as the current versions. To dynamically update these new instances, run the ApplyDynamicUpdate application.
.NET feedback
.NET is an open source project. Select a link to provide feedback:
Training
Learning path
Solution Architect: Design Microsoft Power Platform solutions - Training
Learn how a solution architect designs solutions.
Documentation
Customizing the Workflow Design Experience - .NET Framework
Learn more about: Customizing the Workflow Design Experience
Windows Workflow Foundation Feature Specifics - .NET Framework
This article describes new features that .NET Framework 4 adds to Windows Workflow Foundation and scenarios in which the features might be useful.
Hosting Workflows - .NET Framework
Learn more about: Hosting Workflows