Export (0) Print
Expand All

How to: Update the Definition of a Running Workflow Instance

.NET Framework 4.5

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.

noteNote:
To download a completed version or view a video walkthrough of the tutorial, see Windows Workflow Foundation (WF45) - Getting Started Tutorial.

In this topic

To create the CreateUpdateMaps project

  1. Right-click WF45GettingStartedTutorial in Solution Explorer and choose Add, New Project.

  2. In the Installed node, select Visual C#, Windows (or Visual Basic, Windows).

    noteNote:
    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.

  3. Right-click CreateUpdateMaps in Solution Explorer and choose Add Reference.

  4. 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.

  5. Check the checkbox beside System.Activities from the Search Results list.

  6. Type Serialization into the Search Assemblies box, and check the checkbox beside System.Runtime.Serialization from the Search Results list.

  7. Type System.Xaml into the Search Assemblies box, and check the checkbox beside System.Xaml from the Search Results list.

  8. Click OK to close Reference Manager and add the references.

  9. Add the following using (or Imports) statements at the top of the file with the other using (or Imports) statements.

    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;
    
  10. Add the following two string members to the Program class (or Module1).

    const string mapPath = @"..\..\..\PreviousVersions";
    const string definitionPath = @"..\..\..\NumberGuessWorkflowActivities_du";
    
  11. 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 static ActivityBuilder StartUpdate(string name)
    {
        // Create the XamlXmlReaderSettings.
        XamlXmlReaderSettings readerSettings = new XamlXmlReaderSettings()
        {
            // In the XAML the "local" namespace referes 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;
    }
    
  12. 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 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);
        }
    }
    
  13. 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 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();
    }
    

To update StateMachineNumberGuessWorkflow

  1. Add a CreateStateMachineUpdateMap to the Program class (or Module1).

    private static void CreateStateMachineUpdateMap()
    {  
    }
    
  2. Make a call to StartUpdate and then get a reference to the root StateMachine activity of the workflow.

    ActivityBuilder wf = StartUpdate("StateMachineNumberGuessWorkflow.xaml");
    
    // Get a reference to the root StateMachine activity.
    StateMachine sm = wf.Implementation as StateMachine;
    
  3. 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.
    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.\"");
    
  4. Next, add the new WriteLine activity that displays the closing message.

    // 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;
    
  5. 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");
    
    The following example is the completed CreateStateMachineUpdateMap method.

    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");
    }
    

To update FlowchartNumberGuessWorkflow

  1. 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 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");
    }
    

To update SequentialNumberGuessWorkflow

  1. 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 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");
    }
    

To build and run the CreateUpdateMaps application

  1. 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.

    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();
    }
    
  2. Right-click CreateUpdateMaps in Solution Explorer and choose Set as StartUp Project.

  3. Press CTRL+SHIFT+B to build the solution, and then CTRL+F5 to run the CreateUpdateMaps application.

    noteNote:
    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.

To build the updated workflow assembly

  1. Open a second instance of Visual Studio 2012.

  2. Choose Open, Project/Solution from the File menu.

  3. 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.

  4. 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.

  5. Choose Add Existing Item from the Project menu.

  6. Navigate to the NumberGuessWorkflowActivities_du folder you created in How to: Host Multiple Versions of a Workflow Side-by-Side.

  7. Choose XAML Files (*.xaml;*.xoml) from the Files of type drop-down list.

  8. Select SequentialNumberGuessWorkflow_du.xaml, FlowchartNumberGuessWorkflow_du.xaml, and StateMachineNumberGuessWorkflow_du.xaml and click Add.

    noteNote:
    CTRL+Click to select multiple items at a time.

    This step adds the updated versions of the workflow definitions to the project.

  9. Press CTRL+SHIFT+B to build the project.

  10. 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.

  11. Open Windows Explorer and navigate to the NumberGuessWorkflowActivities_du\bin\Debug folder (or bin\Release depending on your project settings).

  12. 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.

To update WorkflowVersionMap with the new versions

  1. Switch back to the initial instance of Visual Studio 2012.

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

  3. 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.
    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;
    
  4. 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
    {
        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 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();
        }
    }
    
  5. Press CTRL+SHIFT+B to build the project.

To apply the dynamic updates

  1. Right-click WF45GettingStartedTutorial in Solution Explorer and choose Add, New Project.

  2. In the Installed node, select Visual C#, Windows (or Visual Basic, Windows).

    noteNote:
    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.

  3. Right-click ApplyDynamicUpdate in Solution Explorer and choose Add Reference.

  4. Click Solution and check the box next to NumberGuessWorkflowHost. This reference is needed so that ApplyDynamicUpdate can use the NumberGuessWorkflowHost.WorkflowVersionMap class.

  5. 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.

  6. Check the checkbox beside System.Activities from the Search Results list.

  7. Type Serialization into the Search Assemblies box, and check the checkbox beside System.Runtime.Serialization from the Search Results list.

  8. Type DurableInstancing into the Search Assemblies box, and check the checkbox beside System.Activities.DurableInstancing and System.Runtime.DurableInstancing from the Search Results list.

  9. Click OK to close Reference Manager and add the references.

  10. Right-click ApplyDynamicUpdate in Solution Explorer and choose Add, Class. Type DynamicUpdateInfo into the Name box and click Add.

  11. 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.

    class DynamicUpdateInfo
    {
        public DynamicUpdateMap updateMap;
        public WorkflowIdentity newIdentity;
    }
    
  12. Add the following using (or Imports) statements at the top of the file with the other using (or Imports) statements.

    using System.Activities;
    using System.Activities.DynamicUpdate;
    
  13. Double-click Program.cs (or Module1.vb) in Solution Explorer.

  14. Add the following using (or Imports) statements at the top of the file with the other using (or Imports) statements.

    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;
    
  15. Add the following connection string member to the Program class (or Module1).

    const string connectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI";
    
    noteNote:
    Depending on your edition of SQL Server, the connection string server name may be different.

  16. Add the following GetIDs method to the Program class (or Module1). This method returns a list of persisted workflow instance ids.

    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;
    }
    
  17. 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.

    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;
    }
    
  18. 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.

    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;            
    }
    
  19. 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.

    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();
        }
    }
    
  20. Right-click ApplyDynamicUpdate in Solution Explorer and choose Set as StartUp Project.

  21. 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 . . .

To run the application with the updated workflows

  1. Right-click NumberGuessWorkflowHost in Solution Explorer and choose Set as StartUp Project.

  2. Press CTRL+F5 to run the application.

  3. 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.

  4. 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.
  5. 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.
  6. 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.

To enable starting previous versions of the workflows

If you run out of workflows to update, you can modify the NumberGuessWorkflowHost application to enable starting previous versions of the workflows.

  1. Double-click WorkflowHostForm in Solution Explorer, and select the WorkflowType combo box.

  2. In the Properties window, select the Items property and click the ellipsis button to edit the Items collection.

  3. 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
    
  4. Double-click WorkflowHostForm in Solution Explorer, and select View Code.

  5. 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;
        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.

    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;
    };
    
  6. 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.




Build Date:

2012-08-21
Show:
© 2014 Microsoft