閱讀英文

共用方式為


作法:更新執行中工作流程執行個體的定義

動態更新提供的機制可讓工作流程應用程式開發人員更新持續性工作流程執行個體的工作流程定義。 必要的變更可以是實作錯誤修復、新要求,或是適應突如其來的變化。 本教學課程中的步驟示範如何使用動態更新修改 v1 猜數字工作流程的持續性執行個體,以符合操作說明:並存裝載工作流程的多個版本中介紹的新功能。

建立 CreateUpdateMaps 專案

  1. 以滑鼠右鍵按一下方案總管中的 WF45GettingStartedTutorial,並選擇 [新增]、[新增專案]

  2. 在 [已安裝] 節點中選取 [Visual C#]、[Windows] (或 [Visual Basic]、[Windows])。

    注意

    依據設定哪個程式語言為 Visual Studio 主要語言而異,[ Visual C# ] 或 [ Visual Basic ] 節點可能會顯示在 [ 已安裝 ] 節點中的 [ 其他語言 ] 節點下。

    確認已選取 [.NET Framework 版本] 下拉式清單中的 [ .NET Framework 4.5 ]。 從 [Windows] 清單中選取 [主控台應用程式]。 在 [名稱] 方塊中輸入 CreateUpdateMaps,然後按一下 [確定]

  3. 以滑鼠右鍵按一下方案總管中的 CreateUpdateMaps,並選擇 [新增參考]

  4. 在 [新增參考] 清單的 [組件] 節點中,選取 [架構]。 在 [搜尋組件] 方塊中輸入 System.Activities 以篩選組件,讓您更容易選取所需的參考。

  5. 從 [搜尋結果] 清單中勾選 [System.Activities] 旁的核取方塊。

  6. 在 [搜尋組件] 方塊中輸入 Serialization,然後在 [搜尋結果] 清單中勾選 [System.Runtime.Serialization] 旁的核取方塊。

  7. 在 [搜尋組件] 方塊中輸入 System.Xaml,然後在 [搜尋結果] 清單中勾選 [System.Xaml] 旁的核取方塊。

  8. 按一下 [確定] 關閉 [參考管理員],並新增參考。

  9. 將下列 using (或 Imports) 陳述式加入至檔案最上方的其他 using (或 Imports) 陳述式。

    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. 將下列兩個字串成員加入至 Program 類別 (或 Module1)。

    const string mapPath = @"..\..\..\PreviousVersions";
    const string definitionPath = @"..\..\..\NumberGuessWorkflowActivities_du";
    
  11. 將下列 StartUpdate 方法加入至 Program 類別 (或 Module1)。 此方法會將指定的 xaml 工作流程定義載入到 ActivityBuilder,然後呼叫 DynamicUpdate.PrepareForUpdatePrepareForUpdate 會在 ActivityBuilder 中建立工作流程定義的複本。 修改工作流程定義後,會使用此複本和修改過的工作流程定義建立更新對應。

    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;
    }
    
  12. 接下來,將下列 CreateUpdateMethod 加入至 Program 類別 (或 Module1)。 這樣會呼叫 DynamicUpdateServices.CreateUpdateMap 來建立動態更新對應,然後使用指定名稱儲存更新對應。 此更新對應包含工作流程執行階段更新持續工作流程執行個體所需的資訊,該執行個體是使用包含在 ActivityBuilder 中的原始工作流程定義啟動的,這樣它才會使用更新的工作流程定義完成。

    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. 將下列 SaveUpdatedDefinition 方法加入至 Program 類別 (或 Module1)。 這種方法會在建立更新對應之後,儲存更新的工作流程定義。

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

更新 StateMachineNumberGuessWorkflow

  1. CreateStateMachineUpdateMap 加入至 Program 類別 (或 Module1)。

    private static void CreateStateMachineUpdateMap()
    {
    }
    
  2. 呼叫 StartUpdate,然後取得工作流程的根 StateMachine 活動參考。

    ActivityBuilder wf = StartUpdate("StateMachineNumberGuessWorkflow.xaml");
    
    // Get a reference to the root StateMachine activity.
    StateMachine sm = wf.Implementation as StateMachine;
    
  3. 接下來,更新兩個 WriteLine 活動的運算式 (其中顯示該使用者的猜測太高或太低),使其符合在操作說明:並存裝載工作流程的多個版本中進行的更新。

    // 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. 接下來,加入顯示結束訊息的新 WriteLine 活動。

    // 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. 更新工作流程後,呼叫 CreateUpdateMapsSaveUpdatedDefinitionCreateUpdateMaps 會建立和儲存 DynamicUpdateMap,而 SaveUpdatedDefinition 會儲存更新過的工作流程定義。

    // Create the update map.
    CreateUpdateMaps(wf, "StateMachineNumberGuessWorkflow.map");
    
    // Save the updated workflow definition.
    SaveUpdatedDefinition(wf, "StateMachineNumberGuessWorkflow_du.xaml");
    

    下列範例是完成的 CreateStateMachineUpdateMap 方法。

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

更新 FlowchartNumberGuessWorkflow

  1. 將下列 CreateFlowchartUpdateMethod 加入至 Program 類別 (或 Module1)。 此方法類似 CreateStateMachineUpdateMap。 它會從呼叫 StartUpdate 開始、更新流程圖工作流程定義,接著在儲存更新對應和更新的工作流程定後結束。

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

更新 SequentialNumberGuessWorkflow

  1. 將下列 CreateSequentialUpdateMethod 加入至 Program 類別 (或 Module1)。 這個方法類似另外兩個方法。 它會從呼叫 StartUpdate 開始、更新循序工作流程定義,接著在儲存更新對應和更新的工作流程定後結束。

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

建置及執行 CreateUpdateMaps 應用程式

  1. 更新 Main 方法,並加入下列三種方法呼叫。 這些方法會加入至下列區段。 每個方法會更新對應的數字猜測工作流程,並建立描述更新的 DynamicUpdateMap

    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. 以滑鼠右鍵按一下方案總管中的 CreateUpdateMaps,並選擇 [設定為啟始專案]

  3. 按 CTRL + SHIFT + B 建置方案,然後按 CTRL + F5 執行 CreateUpdateMaps 應用程式。

    注意

    CreateUpdateMaps 應用程式在執行時不會顯示任何狀態資訊,但如果您查看 NumberGuessWorkflowActivities_du 資料夾和 PreviousVersions 資料夾,就會看到更新的工作流程定義檔案和更新對應。

    建立更新對應並更新工作流程定義後,下一步就是建置包含更新定義的更新工作流程組件。

建置更新的工作流程組件

  1. 開啟 Visual Studio 2012 的第二個執行個體。

  2. 從 [檔案] 功能表中選擇 [開啟]、[專案/解決方案]

  3. 瀏覽至您在操作說明:並存裝載工作流程的多個版本中建立的 NumberGuessWorkflowActivities_du 資料夾,選取 NumberGuessWorkflowActivities.csproj (或 vbproj),然後按一下 [開啟]

  4. 方案總管中,以滑鼠右鍵按一下 SequentialNumberGuessWorkflow.xaml,然後選擇 [從專案移除]。 以同樣方式處理 FlowchartNumberGuessWorkflow.xamlStateMachineNumberGuessWorkflow.xaml。 此步驟會從專案中移除舊版的工作流程定義。

  5. 從 [專案] 功能表中選擇 [新增現有項目]

  6. 瀏覽至您在操作說明:並存裝載工作流程的多個版本中建立的 NumberGuessWorkflowActivities_du 資料夾。

  7. 從 [檔案型別] 下拉式清單中選擇 [XAML 檔案 (*.xaml;*.xoml)]

  8. 選取 SequentialNumberGuessWorkflow_du.xamlFlowchartNumberGuessWorkflow_du.xamlStateMachineNumberGuessWorkflow_du.xaml,然後按一下 [新增]

    注意

    按住 CTRL + 按一下以同時選取多個項目。

    此步驟會將更新過的工作流程定義版本加入至專案中。

  9. 按 CTRL+SHIFT+B 以建置專案。

  10. 從 [檔案] 功能表中選擇 [關閉解決方案]。 不需要專案的解決方案檔案,因此請按一下 [否] 關閉 Visual Studio,而不儲存解決方案檔案。 從 [檔案] 功能表中選擇 [結束],以關閉 Visual Studio。

  11. 開啟 [Windows 檔案總管],並瀏覽至 NumberGuessWorkflowActivities_du\bin\Debug 資料夾 (或 bin\Release,視您的專案設定而定)。

  12. NumberGuessWorkflowActivities.dll 重新命名為 NumberGuessWorkflowActivities_v15.dll,然後將其複製到您在操作說明:並存裝載工作流程的多個版本中建立的 PreviousVersions 資料夾。

以新版本更新 WorkflowVersionMap

  1. 切換回 Visual Studio 2012 的初始執行個體。

  2. 按兩下 NumberGuessWorkflowHost 專案底下的 WorkflowVersionMap.cs (或 WorkflowVersionMap.vb) 加以開啟。

  3. 將三個新的工作流程識別加入到六個現有工作流程識別宣告的正下方。 在本教學課程中,會使用 1.5.0.0 做為動態更新識別的 WorkflowIdentity.Version。 這些新的 v15 工作流程識別,會為動態更新的持續性工作流程執行個體提供正確的工作流程定義。

    // 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. 在建構函式結尾,加入下列程式碼。 此程式碼會初始化動態更新工作流程識別、載入對應的工作流程定義,然後將定義加入至工作流程版本字典。

    // 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);
    

    下列範例是已完成的 WorkflowVersionMap 類別。

    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. 按 CTRL+SHIFT+B 以建置專案。

套用動態更新

  1. 以滑鼠右鍵按一下方案總管中的 WF45GettingStartedTutorial,並選擇 [新增]、[新增專案]

  2. 在 [已安裝] 節點中選取 [Visual C#]、[Windows] (或 [Visual Basic]、[Windows])。

    注意

    依據設定哪個程式語言為 Visual Studio 主要語言而異,[ Visual C# ] 或 [ Visual Basic ] 節點可能會顯示在 [ 已安裝 ] 節點中的 [ 其他語言 ] 節點下。

    確認已選取 [.NET Framework 版本] 下拉式清單中的 [ .NET Framework 4.5 ]。 從 [Windows] 清單中選取 [主控台應用程式]。 在 [名稱] 方塊中輸入 ApplyDynamicUpdate,然後按一下 [確定]

  3. 以滑鼠右鍵按一下方案總管中的 ApplyDynamicUpdate,並選擇 [新增參考]

  4. 按一下 [解決方案],並勾選 [NumberGuessWorkflowHost] 旁的方塊。 ApplyDynamicUpdate 需要此參考才能使用 NumberGuessWorkflowHost.WorkflowVersionMap 類別。

  5. 在 [新增參考] 清單的 [組件] 節點中,選取 [架構]。 在 [搜尋組件] 方塊中輸入 System.Activities。 如此即會篩選組件,讓您更容易選取所需的參考。

  6. 從 [搜尋結果] 清單中勾選 [System.Activities] 旁的核取方塊。

  7. 在 [搜尋組件] 方塊中輸入 Serialization,然後在 [搜尋結果] 清單中勾選 [System.Runtime.Serialization] 旁的核取方塊。

  8. 在 [搜尋組件] 方塊中輸入 DurableInstancing,然後在 [搜尋結果] 清單中勾選 [System.Activities.DurableInstancing] 和 [System.Runtime.DurableInstancing] 旁的核取方塊。

  9. 按一下 [確定] 關閉 [參考管理員],並新增參考。

  10. 以滑鼠右鍵按一下方案總管中的 ApplyDynamicUpdate,並選擇 [類別]。 在 [名稱] 方塊中輸入 DynamicUpdateInfo,然後按一下 [新增]

  11. 將下列兩個成員加入至 DynamicUpdateInfo 類別。 下列範例是已完成的 DynamicUpdateInfo 類別。 此類別包含更新對應的資訊,以及更新工作流程執行個體時所使用的新工作流程識別。

    class DynamicUpdateInfo
    {
        public DynamicUpdateMap updateMap;
        public WorkflowIdentity newIdentity;
    }
    
  12. 將下列 using (或 Imports) 陳述式加入至檔案最上方的其他 using (或 Imports) 陳述式。

    using System.Activities;
    using System.Activities.DynamicUpdate;
    
  13. 在方案總管中按兩下 Program.cs (或 Module1.vb)。

  14. 將下列 using (或 Imports) 陳述式加入至檔案最上方的其他 using (或 Imports) 陳述式。

    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. 將下列連接字串成員加入至 Program 類別 (或 Module1)。

    const string connectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI";
    

    重要

    Microsoft建議您使用可用的最安全驗證流程。 如果您要連接到 Azure SQL,Azure 資源受控識別是建議的驗證方法。

    注意

    依據您的 SQL Server 版本,連接字串伺服器名稱可能有所不同。

  16. 將下列 GetIDs 方法加入至 Program 類別 (或 Module1)。 此方法會傳回持續性工作流程執行個體識別碼的清單。

    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. 將下列 LoadMap 方法加入至 Program 類別 (或 Module1)。 此方法會建立字典,此字典會將 v1 工作流程識別對應至更新對應,以及用來更新對應持續性工作流程執行個體的新工作流程識別。

    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. 將下列 LoadMaps 方法加入至 Program 類別 (或 Module1)。 此方法會載入三個更新對應,並建立將 v1 工作流程識別對應至更新對應的字典。

    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. 將下列程式碼新增至 Main。 此程式碼會逐一查看持續性工作流程執行個體,並檢查每個 WorkflowIdentity。 如果 WorkflowIdentity 對應至 v1 工作流程執行個體,就會以更新的工作流程定義和更新的工作流程識別來設定 WorkflowApplication。 接下來會以執行個體和更新對應來呼叫 WorkflowApplication.Load,以套用動態更新對應。 一旦套用更新後,會藉由呼叫 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. 以滑鼠右鍵按一下方案總管中的 ApplyDynamicUpdate,並選擇 [設定為啟始專案]

  21. 按下 Ctrl + Shift + B 建置方案,然後按下 Ctrl + F5 執行 ApplyDynamicUpdate 應用程式,並更新持續性工作流程執行個體。 您應該會看到如下所示的輸出。 1.0.0.0 版工作流程會更新為 1.5.0.0 版,但不會更新 2.0.0.0 版工作流程。

    檢查:StateMachineNumberGuessWorkflow;版本=1.0.0.0
    更新為:StateMachineNumberGuessWorkflow;版本=1.5.0.0
    檢查:StateMachineNumberGuessWorkflow;版本=1.0.0.0
    更新為:StateMachineNumberGuessWorkflow;版本=1.5.0.0
    檢查:FlowchartNumberGuessWorkflow;版本=1.0.0.0
    更新為:FlowchartNumberGuessWorkflow;版本=1.5.0.0
    檢查:FlowchartNumberGuessWorkflow;版本=1.0.0.0
    更新為:FlowchartNumberGuessWorkflow;版本=1.5.0.0
    檢查:SequentialNumberGuessWorkflow;版本=1.0.0.0
    更新為:SequentialNumberGuessWorkflow;版本=1.5.0.0
    檢查:SequentialNumberGuessWorkflow;版本=1.0.0.0
    更新為:SequentialNumberGuessWorkflow;版本=1.5.0.0
    檢查:SequentialNumberGuessWorkflow;版本=1.0.0.0
    更新為:SequentialNumberGuessWorkflow;版本=1.5.0.0
    檢查:StateMachineNumberGuessWorkflow;版本=1.0.0.0
    更新為:StateMachineNumberGuessWorkflow;版本=1.5.0.0
    檢查:FlowchartNumberGuessWorkflow;版本=1.0.0.0
    更新為:FlowchartNumberGuessWorkflow;版本=1.5.0.0
    檢查:StateMachineNumberGuessWorkflow;版本=2.0.0.0
    檢查:StateMachineNumberGuessWorkflow;版本=2.0.0.0
    檢查:FlowchartNumberGuessWorkflow;版本=2.0.0.0
    檢查:FlowchartNumberGuessWorkflow;版本=2.0.0.0
    檢查:SequentialNumberGuessWorkflow;版本=2.0.0.0
    檢查:SequentialNumberGuessWorkflow;版本=2.0.0.0
    按任意鍵繼續。 . .

以更新的工作流程執行應用程式

  1. 以滑鼠右鍵按一下方案總管中的 NumberGuessWorkflowHost,並選擇 [設定為啟始專案]

  2. 按 CTRL+F5 執行應用程式。

  3. 按一下 [新遊戲] 啟動新的工作流程,並記下狀態視窗下方的版本資訊 (指出工作流程為 v2 工作流程)。

  4. 選取您在操作說明:並存裝載工作流程的多個版本主題開頭啟動的其中一個 v1 工作流程。 確認狀態視窗下方的版本資訊指出工作流程為 1.5.0.0 版工作流程。 請注意,除太大或太小之外,沒有任何資訊指示先前的猜測。

    請輸入介於 1 到 10 之間的數字
    您猜得太低。

  5. 記下 InstanceId,然後輸入猜測值,直到工作流程完成。 狀態視窗會顯示猜測內容的相關資訊,因為動態更新已更新 WriteLine 活動。

    請輸入介於 1 到 10 之間的數字
    您猜得太低。
    請輸入介於 1 到 10 之間的數字
    5 太小了。
    請輸入介於 1 到 10 之間的數字
    7 太大了。
    請輸入介於 1 到 10 之間的數字
    恭喜您,您試了 4 次就猜到正確數字。

  6. 開啟 [Windows 檔案總管],並瀏覽至 NumberGuessWorkflowHost\bin\debug 資料夾 (或 bin\release,視您的專案設定而定),然後使用記事本開啟已完成的工作流程對應的追蹤檔案。 若您未記下 InstanceId,可以利用 Windows 檔案總管中的 [修改日期] 資訊找到正確的追蹤檔案。 追蹤資訊的最後一行包含新加入之 WriteLine 活動的輸出。

    請輸入介於 1 到 10 之間的數字
    您猜得太低。
    請輸入介於 1 到 10 之間的數字
    5 太小了。
    請輸入介於 1 到 10 之間的數字
    7 太大了。
    請輸入介於 1 到 10 之間的數字
    6 是正確的。 您試了 4 次就猜到正確數字。

使其能夠啟動工作流程的舊版本

如果您執行完工作流程來進行更新,可以修改 NumberGuessWorkflowHost 應用程式,使其能啟動工作流程的舊版本。

  1. 按兩下方案總管中的 WorkflowHostForm,然後選取 [WorkflowType] 下拉式方塊。

  2. 在 [屬性] 視窗中選取 Items 屬性,然後按一下省略符號按鈕,以編輯 [項目] 集合。

  3. 將下列三個項目加入至集合。

    StateMachineNumberGuessWorkflow v1
    FlowchartNumberGuessWorkflow v1
    SequentialNumberGuessWorkflow v1
    

    完成的 Items 集合中會有六個項目。

    StateMachineNumberGuessWorkflow
    FlowchartNumberGuessWorkflow
    SequentialNumberGuessWorkflow
    StateMachineNumberGuessWorkflow v1
    FlowchartNumberGuessWorkflow v1
    SequentialNumberGuessWorkflow v1
    
  4. 按兩下方案總管中的 WorkflowHostForm,然後選取 [檢視程式碼]

  5. 將三個新案例新增至 NewGame_Click 處理常式中的 switch (或 Select Case) 陳述式,以將 [WorkflowType] 下拉式方塊中的新項目對應至相符的工作流程識別。

    case "SequentialNumberGuessWorkflow v1":
        identity = WorkflowVersionMap.SequentialNumberGuessIdentity_v1;
        break;
    
    case "StateMachineNumberGuessWorkflow v1":
        identity = WorkflowVersionMap.StateMachineNumberGuessIdentity_v1;
        break;
    
    case "FlowchartNumberGuessWorkflow v1":
        identity = WorkflowVersionMap.FlowchartNumberGuessIdentity_v1;
        break;
    

    下列範例包含完整的 switch (或 Select Case) 陳述式。

    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. 按 CTRL+F5 建置並執行應用程式。 您現在可以啟動工作流程的 v1 版本以及目前版本。 若要動態更新這些新的執行個體,請執行 ApplyDynamicUpdate 應用程式。


其他資源