共用方式為


.NET 4 中的 Windows Workflow Foundation (WF) 開發人員簡介

Matt Milner、Pluralsight

2009 年 11 月

更新為發行日期:2010 年 4 月

概觀

隨著軟體發展人員瞭解,撰寫應用程式可能很困難,而且我們不斷尋找工具和架構來簡化程式,並協助我們專注于我們嘗試解決的商業挑戰。  我們已從以電腦語言撰寫程式碼,例如 C# 和 Visual Basic 等較高層級的語言,以簡化我們的開發、移除較低層級的考慮,例如記憶體管理,以及提升開發人員的生產力。  對於 Microsoft 開發人員,移至 .NET 可讓 Common Language Runtime (CLR) 配置記憶體、清除不需要的物件,以及處理低階建構,例如指標。 

應用程式的大部分複雜度都存在於幕後發生的邏輯和處理中。  非同步或平行執行等問題,以及通常協調回應使用者要求或服務要求的工作,可讓應用程式開發人員快速回到低階的控制碼、回呼、同步處理等程式碼撰寫。身為開發人員,我們需要應用程式內部宣告式程式設計模型的功能和彈性,因為我們在Windows Presentation Foundation (WPF) 的使用者介面。 Windows Workflow Foundation (WF) 提供宣告式架構來建置應用程式和服務邏輯,並為開發人員提供較高層級的語言來處理非同步、平行工作和其他複雜的處理。

擁有執行時間來管理記憶體和物件,可讓我們更專注于撰寫程式碼的重要商務層面。  同樣地,擁有可以管理協調非同步工作複雜性的執行時間,提供一組可改善開發人員生產力的功能。  WF 是一組工具,可用來宣告工作流程 (商務邏輯) 、協助定義邏輯和控制流程的活動,以及執行結果應用程式定義的執行時間。  簡單來說,WF 是關於使用較高層級的語言來撰寫應用程式,目標是讓開發人員更具生產力、更容易管理應用程式,以及更快速地變更實作。  WF 執行時間不僅會為您執行工作流程,也提供在撰寫應用程式邏輯時重要的服務與功能,例如狀態持續性、書簽和繼續商務邏輯,這全都會導致執行緒和程式靈活度增加並相應放大商務程式。 

如需使用 WF 建置應用程式如何讓您更具生產力的概念資訊,建議您閱讀 David Chappell 的「工作流程方式」,請參閱其他資源一節。 

WF4 的新功能

在 Microsoft® .NET Framework的第 4 版中,Windows Workflow Foundation 會從舊版的 .NET 3.0 和 3.5 隨附的技術引進大量變更。  事實上,小組重新流覽程式設計模型、執行時間和工具的核心,並重新建構每一個模型,以提高效能和生產力,以及解決使用舊版客戶參與的重要意見反應。  必須進行重大變更,才能為採用 WF 的開發人員提供最佳體驗,並讓 WF 繼續成為您可以在應用程式中建置的強式基礎元件。 我將在這裡介紹高層級變更,而且在整個檔中,每個主題都會取得更深入的處理。 

在繼續之前,請務必瞭解回溯相容性也是此版本中的重要目標。  新的架構元件主要位於 System.Activities.* 元件中,而回溯相容的架構元件則位於 System.Workflow.* 元件中。  System.Workflow.* 元件是.NET Framework 4 的一部分,並提供完整的回溯相容性,讓您可以將應用程式移轉至 .NET 4,而不需要變更工作流程程式碼。 在本檔中,我將使用 WF4 名稱來參照 System.Activities.* 元件和 WF3 中找到的新元件,以參考 System.Workflow.* 元件中找到的元件。 

設計工具

其中一個最明顯的改進領域是在工作流程設計工具中。 可用性和效能是 VS 2010 版本小組的主要目標。  設計工具現在支援使用較大型工作流程的能力,而效能降低,而設計工具全都以Windows Presentation Foundation (WPF) 為基礎,充分利用可以使用宣告式 UI 架構所建置的豐富使用者體驗。  活動開發人員會使用 XAML 來定義其活動在視覺設計環境中的外觀,並與使用者互動的方式。  此外,在您自己的應用程式中重新裝載工作流程設計工具,讓非開發人員能夠檢視與工作流程互動,現在更容易。 

資料流程

在 WF3 中,工作流程中的資料流程程不透明。  WF4 提供清楚、簡潔的資料流程模型,並在使用引數和變數時進行範圍界定。  所有開發人員都熟悉這些概念,可簡化資料儲存體的定義,以及工作流程和活動輸入和輸出的資料流程程。  資料流程模型也會讓指定活動的預期輸入和輸出更加明顯,並改善執行時間的效能,因為資料更容易管理。 

流程圖

已新增名為 Flowchart 的新控制流程活動,讓開發人員能夠使用流程圖模型來定義工作流程。  流程圖更類似于許多分析師和開發人員在建立解決方案或設計商務程式時所經歷的概念和思考程式。  因此,提供活動來讓模型化已經完成的概念性思考和規劃變得很合理。  流程圖可啟用概念,例如返回先前的步驟,並根據單一條件或切換/案例邏輯來分割邏輯。 

程式設計模型

WF 程式設計模型已經過重新設定,使其更簡單且更強固。  活動是程式設計模型中的核心基底類型,同時代表工作流程和活動。  此外,您不再需要建立 WorkflowRuntime 來叫用工作流程,只要建立實例並加以執行,簡化您不想設定特定環境的單元測試和應用程式案例。  最後,工作流程程式設計模型會成為活動的完整宣告式組合,而不需要程式碼,進而簡化工作流程撰寫。

Windows Communication Foundation (WCF) 整合

WF 的優點大多適用于建立服務和取用或協調服務互動。  大量努力強化 WCF 與 WF 之間的整合。  新的傳訊活動、訊息相互關聯和改良的裝載支援,以及完整的宣告式服務定義是改善的主要領域。 

使用工作流程消費者入門

瞭解 WF 的最佳方式是開始使用它並套用概念。  我將涵蓋工作流程基礎的相關幾個核心概念,然後逐步解說建立幾個簡單的工作流程,以說明這些概念彼此的關聯性。 

工作流程結構

活動是 WF 的建置組塊,而且所有活動最終都衍生自 Activity。  術語注意事項 - 活動是 WF 中的工作單位。 活動可以組合成較大的活動。 當活動做為最上層進入點時,它稱為「工作流程」,就像 Main 只是另一個函式,代表 CLR 程式的最上層進入點。   例如,圖 1 顯示建置在程式碼中的簡單工作流程。 

序列 s = 新序列

{

    活動 = {

        new WriteLine {Text = 「Hello」},

        new Sequence {

            活動 =

            {

                new WriteLine {Text = 「Workflow」},

                new WriteLine {Text = 「World」}

            }

        }

    }

};

圖 1:簡單的工作流程

請注意,圖 1 中會使用 Sequence 活動做為根活動,以定義工作流程的根控制流程樣式。 任何活動都可以當做根或工作流程使用並執行,甚至是簡單的 WriteLine。   藉由使用其他活動的集合在 Sequence 上設定 Activities 屬性,我已定義工作流程結構。  此外,子活動可以有子活動,建立構成整體工作流程定義的活動樹狀結構。 

工作流程範本和工作流程Designer

WF4 隨附許多活動和 Visual Studio 2010 包含定義活動的範本。 根控制流程所使用的兩個最常見活動是 Sequence 和 Flowchart。  雖然任何活動都可以以工作流程的形式執行,但這兩者提供最常見的設計模式來定義商務邏輯。   圖 2 顯示循序工作流程模型的範例,定義所接收訂單的處理、儲存,然後傳送給其他服務的通知。   

 

圖 2:循序工作流程設計

Flowchart 工作流程類型是在 WF4 中引進,以解決現有使用者的常見要求,例如能夠返回工作流程中的先前步驟,而且更類似于分析師和開發人員在定義商務邏輯時所完成的概念設計。  例如,請考慮涉及應用程式使用者輸入的案例。  為了回應使用者所提供的資料,您的程式應該繼續執行程式,或返回上一個步驟,再次提示輸入。 使用循序工作流程時,這會牽涉到類似圖 3 所示的內容,其中 DoWhile 活動用來繼續處理,直到符合某些條件為止。 

圖 3:決策分支的循序

圖 3 中的設計可運作,但身為查看工作流程的開發人員或分析師,模型並不代表所描述的原始邏輯或需求。   圖 4 中的流程圖工作流程提供與圖 3 中使用的循序模型類似的技術成果,但工作流程的設計更符合原本定義的思考和需求。 

圖 4:流程圖工作流程

工作流程中的資料流程

大部分人思考工作流程時,第一個想法是商務程式或應用程式的流程。  不過,就像流程一樣重要,就是驅動進程的資料,以及在工作流程執行期間收集並儲存的資訊。  在 WF4 中,資料的儲存和管理是設計考慮的主要領域。 

有三個主要概念可瞭解資料:變數、引數和運算式。  每個變數的簡單定義都是用來儲存資料、引數用於傳遞資料,而運算式則用於運算元據。 

變數 – 儲存資料

工作流程中的變數非常類似您在命令式語言中使用的變數:它們描述要儲存資料的具名位置,並遵循特定範圍規則。  若要建立變數,您必須先判斷變數需要提供的範圍。  就像您在類別或方法層級可用的程式碼中有變數一樣,您的工作流程變數可以在不同的範圍內定義。  請考慮圖 5 中的工作流程。  在此範例中,您可以在工作流程的根層級或收集摘要資料順序活動所定義的範圍定義變數。 

圖 5:範圍設定為活動的變數

引數 – 傳遞資料

引數定義于活動上,並定義進出活動的資料流程。  您可以將引數想像為活動,就像在命令式程式碼中使用引數的方法一樣。  引數可以是 In、Out 或 In/Out,而且具有名稱和類型。  建置工作流程時,您可以在根活動上定義引數,以在叫用資料時將資料傳遞至工作流程。  您可以在工作流程上定義引數,就像使用引數視窗進行變數一樣。 

當您將活動新增至工作流程時,您必須設定活動的引數,主要是藉由參考範圍內變數或使用運算式來完成,接下來將討論。  事實上,Argument 基類包含 Expression 屬性,這是傳回引數型別值的 Activity,因此所有這些選項都相關且依賴 Activity。 

使用工作流程設計工具編輯引數時,您可以在屬性方格中輸入代表常值的運算式,或使用變數名稱來參考範圍內變數,如圖 6 所示,其中 emailResult 和 emailAddress 是工作流程中定義的變數。 

圖 6:設定活動的引數

運算式 – 對資料採取行動

運算式是您可以在工作流程中用來處理資料的活動。  運算式可用於使用 Activity 的位置,而且對傳回值感興趣,這表示您可以使用運算式來設定引數,或定義 While 或 If 活動等活動的條件。  請記住,WF4 中的大部分專案都衍生自 Activity,而運算式並無不同,它們是 Activity < TResult > 的衍生專案,這表示它們會傳回特定類型的值。  此外,WF4 也包含數個參考變數和引數以及 Visual Basic 運算式的常見運算式。  由於這一層特製化運算式,您用來定義引數的運算式可以包含程式碼、常值和變數參考。  表 1 提供您在使用工作流程設計工具定義引數時可能會使用的運算式類型的小型取樣。  在程式碼中建立運算式時,還有數個選項,包括使用 Lambda 運算式。 

運算式 運算式類型

"hello world"

常值字串值

10

常值 Int32 值

System.String.Concat (「hello」, 「 」, 「world」)

命令式方法調用

「hello」 「 & world」

Visual Basic 運算式

argInputString

引數參考 (引數名稱)

varResult

變數參考 (變數名稱)

「hello: 」 & argInputString

混合的常值和引數/變數

表 1:範例運算式

建置您的第一個工作流程

既然我已涵蓋活動與資料流程的核心概念,我可以使用這些概念來建立工作流程。  我將從簡單的 hello world 工作流程開始,專注于概念,而不是 WF 真正的價值主張。  若要開始,請在 Visual Studio 2010 中建立新的單元測試專案。  若要使用 WF 的核心,請新增 System.Activities 元件的參考,並在測試類別檔案中新增 System.Activities、System.Activities.Statements 和 System.IO using 語句。  然後新增測試方法,如圖 7 所示,以建立基本工作流程並加以執行。 

[TestMethod]

public void TestHelloWorldStatic ()

{

    StringWriter 寫入器 = 新的 StringWriter () ;

    Console.SetOut (寫入器) ;

    Sequence wf = new Sequence

    {

        活動 = {

            new WriteLine {Text = 「Hello」},

            new WriteLine {Text = 「World」}

        }

    };

    WorkflowInvoker.Invoke (wf) ;

    Assert.IsTrue (String.Compare (

        「Hello\r\nWorld\r\n」,

        作家。GetStringBuilder () 。ToString () ) == 0,

        「寫入的字串不正確」) ;

}

圖 7:在程式碼中建立 hello world

WriteLine 活動的 Text 屬性是 InArgument < 字串 > ,在此範例中,我已將常值傳遞至該屬性。

下一個步驟是更新此工作流程以使用變數,並將這些變數傳遞至活動引數。  圖 8 顯示更新為使用變數的新測試。

[TestMethod]

public void TestHelloWorldVariables ()

{

    StringWriter 寫入器 = 新的 StringWriter () ;

    Console.SetOut (寫入器) ;

Sequence wf = new Sequence

{

    變數 = {

        new Variable < string > {Default = 「Hello」, Name = 「greeting」},

        new Variable < string > { Default = 「Bill」, Name = 「name」 } },

        活動 = {

            new WriteLine { Text = new VisualBasicValue < string > (「greeting」) ,

            new WriteLine { Text = new VisualBasicValue < string > (

            「name + \」Gates\「」) }

        }

    };

    WorkflowInvoker.Invoke (wf) ;

}

圖 8:使用變數的程式碼工作流程

在此情況下,變數會定義為 Variable < 字串 > 類型,並指定預設值。  變數會在 Sequence 活動中宣告,然後從兩個活動的 Text 引數參考。  或者,我可以使用運算式來初始化變數,如圖 9 所示,其中會使用 VisualBasicValue < TResult > 類別來傳入代表運算式的字串。  在第一個案例中,運算式會參考變數的名稱,在第二個案例中,變數會與常值串連。  文字運算式中使用的語法是 Visual Basic,即使以 C# 撰寫程式碼也是如此。

活動 = {

    new WriteLine { Text = new VisualBasicValue < string > (「greeting」) ,

        TextWriter = 寫入器 },

    new WriteLine { Text = new VisualBasicValue < string > (「name + \」Gates\「」) ,

        TextWriter = 寫入器 }

}

圖 9:使用運算式定義引數

雖然程式碼範例有助於說明重要點,而且通常對開發人員感到熟悉,但大部分的人都會使用設計工具來建立工作流程。  在設計工具中,您會將活動拖曳到設計介面,並使用更新但熟悉的屬性方格來設定引數。  此外,工作流程設計工具組含底部的可展開區域,以編輯工作流程和變數的引數。   若要在設計工具中建立類似的工作流程,請將新專案新增至方案,選擇 [活動程式庫] 範本,然後新增活動。 

第一次顯示工作流程設計工具時,它不包含任何活動。  定義活動的第一個步驟是選擇根活動。  在此範例中,從工具箱中的 Flowchart 類別拖曳流程圖類別,將流程圖活動新增至設計工具。  在 Flowchart 設計工具中,從工具箱拖曳兩個 WriteLine 活動,並將其中一個新增至流程圖下方。  現在您必須將活動連線在一起,讓流程圖知道要遵循的路徑。  您可以先將滑鼠停留在 Flowchart 頂端綠色的「開始」圓形上,以查看抓取控點,然後按一下第一個 WriteLine 並將其拖曳到第一個 WriteLine,然後將它放在出現在活動頂端的拖曳控點上。  執行相同的動作,將第一個 WriteLine 連接到第二個 WriteLine。  設計介面看起來應該像圖 10。 

圖 10:Hello Flowchart 版面配置

結構就緒之後,您必須設定一些變數。  按一下設計介面,然後按一下設計工具底部附近的 [變數] 按鈕,即可編輯流程圖的變數集合。  新增兩個名為 「greeting」 和 「name」 的變數,分別將預設值設定為 「Hello」 和 「Bill」 – 請務必在將值設定為此運算式時包含引號,因此必須加上引號。  圖 11 顯示以兩個變數及其預設值設定的變數視窗。 

圖 11:工作流程設計工具中定義的變數

若要在 WriteLine 活動中使用這些變數,請在第一個 (活動之 Text 引數的屬性方格中輸入 「greeting」 (,然後再次輸入 「greeting」) 引號,然後在第二個 WriteLine 上輸入 Text 引數的引號) 。   在執行時間,評估 Text 引數時,會解析變數中的值,並供 WriteLine 活動使用。

在測試專案中,加入包含工作流程的專案參考。  然後,您可以新增類似圖 12 所示的測試方法,以叫用此工作流程並測試輸出。  在圖 12 中,您可以看到正在建立工作流程,方法是具現化所建立類別的實例。  在此情況下,工作流程是在設計工具中定義,並編譯成衍生自 Activity 的類別,然後叫用該類別。 

[TestMethod]

public void TestHelloFlowChart ()

{

    StringWriter tWriter = new StringWriter () ;

    Console.SetOut (tWriter) ;

    Workflows.HelloFlow wf = new Workflows.HelloFlow () ;

    WorkflowInvoker.Invoke (wf) ;

    省略判斷提示

}

圖 12:測試流程圖

到目前為止,我已在工作流程中使用變數,並使用這些變數將值提供給 WriteLine 活動的引數。  工作流程也可以定義引數,這可讓您在叫用資料時將資料傳入工作流程,並在工作流程完成時接收輸出。  若要更新上一個範例中的流程圖,請移除 「name」 變數, (選取它,然後按 Delete 鍵) ,並改為建立字串類型的 「name」 引數。  除了使用 [引數] 按鈕來檢視引數編輯器之外,您執行這項操作的方式大致相同。  請注意,引數也可以有方向,而且您不需要提供預設值,因為值會在執行時間傳遞至活動。  因為您所使用的引數名稱與變數的名稱相同,所以 WriteLine 活動 Text 引數仍然有效。  現在在執行時間,這些引數將會評估並解析工作流程上的 「name」 引數值,並使用該值。  新增類型字串的其他引數,其方向為 Out 和 「fullGreeting」 的名稱;這會傳回給呼叫端程式碼。 

圖 13:定義工作流程的引數

在流程圖中,新增 Assign 活動,並將其連線到最後一個 WriteLine 活動。  針對 To 引數,輸入 「fullGreeting」 (沒有引號) ,且針對 Value 引數輸入 「greeting & name」 (而不加上引號) 。 這會將 greeting 變數與 name 引數串連指派給 fullGreeting 輸出引數。 

現在在單元測試中,更新程式碼,以在叫用工作流程時提供 引數。  引數會以 Dictionary < 字串的形式傳遞至工作流程,其中 > 索引鍵是引數的名稱。  只要變更呼叫以叫用工作流程,即可這麼做,如圖 14 所示。 請注意,輸出引數也包含在以引數名稱為索引鍵的 Dictionary < 字串中 > 。 

IDictionary < 字串, 物件 > 結果 = WorkflowInvoker.Invoke (wf,

    new Dictionary < string,object > {

        {「name」, 「Bill」 } }

    );

string outValue = results[「fullGreeting」]。ToString () ;

圖 14:將引數傳遞至工作流程

既然您已瞭解如何將活動、變數和引數組合在一起的基本概念,我將會引導您流覽架構中包含的活動,以啟用更有趣的工作流程,著重于商務邏輯,而不是低階概念。

工作流程活動選擇區導覽

使用任何程式設計語言時,您預期會有定義應用程式邏輯的核心建構。  使用 WF 之類的較高層級開發架構時,該期望不會變更。   就像您在 .NET 語言中有語句,例如 If/Else、Switch 和 While,為了管理控制流程,您在宣告式工作流程中定義邏輯時也需要這些相同的功能。  這些功能是以架構隨附的基底活動程式庫形式提供。  在本節中,我將提供您快速導覽隨附于架構的活動,讓您瞭解現成提供的功能。

活動基本專案和集合活動

移至宣告式程式設計模型時,很容易開始想知道如何在撰寫程式碼時執行第二個本質的常見物件操作工作。  對於您自己使用物件且需要設定屬性、叫用命令或管理專案集合的工作,有一組特別設計的活動會考慮這些工作。 

活動 描述

指派

將值指派給位置 – 啟用設定變數。

延遲

延遲指定時間量執行的路徑。

InvokeMethod

在 .NET 物件或 .NET 型別上的靜態方法上叫用方法,選擇性地使用 T 的傳回型別。

WriteLine

將指定的文字寫入至文字寫入器 – 預設為 Console.Out

AddToCollection < T>

將專案加入至具類型的集合。

RemoveFromCollection < T>

從具型別集合中移除專案。

ClearCollection < T>

從集合中移除所有專案。

ExistsInCollection < T>

會傳回 Boolean 值,指出指定的專案是否存在於集合中。 

 

控制流程活動

定義商務邏輯或商務程式時,控制執行流程非常重要。 控制流程活動包含一些基本概念,例如 Sequence,當您需要依序執行步驟時提供通用容器,以及 If 和 Switch 活動等常見分支邏輯。  控制流程活動也包含根據 ForEach) 和 (Conditions (While) 的資料迴圈邏輯。  簡化複雜程式設計最重要的是平行活動,這全都讓多個非同步活動同時完成工作。 

活動 描述

順序

用於以數列方式執行活動

While/DoWhile

當條件 (運算式) 為 true 時,執行子活動

ForEach < T>

逐一查看可列舉的集合,並針對集合中的每個專案執行子活動一次,並等候子專案完成,再開始下一個反復專案。 以具名引數的形式提供個別專案的具型別存取權,以驅動反復專案。

如果

視條件的結果 (運算式) 而定,執行兩個子活動之一。

切換 < T>

評估運算式,並使用相符的索引鍵排程子活動。 

平行

一次排程所有子活動,但也會提供完成條件,讓活動能夠在符合特定條件時取消任何未完成的子活動。 

ParallelForEach < T>

逐一查看可列舉的集合,並針對集合中的每個專案執行子活動一次,同時排程所有實例。 如同 ForEach < T > ,此活動會以具名引數的形式提供目前資料項目的存取權。

挑選

排程所有子 PickBranch 活動,並取消第一個子系的觸發程式完成。  PickBranch 活動同時具有觸發程式和動作;每個 都是活動。  觸發程式活動完成時,Pick 會取消其所有其他子活動。 

 下列兩個範例顯示其中幾個活動,用來說明如何將這些活動組合在一起。  第一個範例圖 15 包含使用 ParallelForEach < T > 使用 URL 清單,並以非同步方式取得指定位址的 RSS 摘要。  傳回摘要之後,ForEach < T > 會用來逐一查看摘要專案並加以處理。 

請注意,程式碼會宣告並定義具有 System.ServiceModel.Syndication 類型參考的 VisualBasicSettings 實例。  然後,宣告參考該命名空間中變數類型的 VisualBasicValue < T > 實例時,會使用此設定物件,以啟用這些運算式的類型解析。  另請注意,ParallelForEach 活動的本文會採用建立自訂活動一節中所述的 ActivityAction。  這些動作使用 DelegateInArgument 和 DelegateOutArgument 的方式與活動使用 InArgument 和 OutArgument 的方式大致相同。 

圖 15:控制流程活動

第二個範例圖 16 是等候有逾時回應的常見模式。  例如,等候經理核准要求,並在回應未到達分配時間時傳送提醒。  DoWhile 活動會導致在 Pick 活動用來同時執行 ManagerResponse 活動和 Delay 活動與觸發程式時,重複等候回應。  當 Delay 第一次完成時,就會傳送提醒,而當 ManagerResponse 活動第一次完成時,旗標會設定為中斷 DoWhile 迴圈。 

圖 16:挑選和 DoWhile 活動

圖 16 中的範例也會示範如何在工作流程中使用書簽。  書簽是由活動所建立,以標記工作流程中的位置,讓處理可以在稍後從該時間點繼續。  Delay 活動具有書簽,該書簽會在經過特定時間之後繼續。  ManagerResponse 活動是自訂活動,會在資料送達後等候輸入並繼續工作流程。  稍後討論的傳訊活動是書簽執行的主要活動。  當工作流程未主動處理工作時,當它只等候書簽繼續時,它就會被視為閒置,而且可以保存到永久性存放區。  在建立自訂活動一節中,將更詳細地討論書簽。 

移轉

對於使用 WF3 的開發人員,Interop 活動可以扮演重複使用現有資產的重要角色。 在 System.Workflow.Runtime 元件中找到的活動會包裝現有的活動類型,並將活動上的屬性顯示為 WF4 模型中的引數。  因為屬性是引數,所以您可以使用運算式來定義值。  圖 17 顯示呼叫 WF3 活動的 Interop 活動設定。  輸入引數是使用工作流程定義內變數的參考來定義。    WF3 內建的活動具有屬性,而不是引數,因此每個屬性都會獲得對應的輸入和輸出引數,讓您區分傳送至活動的資料,以及您在活動執行之後所預期擷取的資料。 在新的 WF4 專案中,您無法在工具箱中找到此活動,因為目標架構設定為 .NET Framework 4 用戶端設定檔。  將專案屬性中的目標架構變更為 .NET Framework 4,活動會顯示在工具箱中。

圖 17:Interop 活動組態

流程圖

設計流程圖工作流程時,有數個建構可用來管理流程圖內的執行流程。  這些建構本身會根據單一條件或 switch 語句提供簡單的步驟、簡單的決策點。  流程圖的實際能力是能夠將這些節點類型連線到所需的流程。

建構/活動 Description

流程圖

一系列流程步驟的容器,每個流程步驟可以是任何活動或下列其中一個建構,但若要執行,則必須在流程圖內連線。 

FlowDecision

根據條件提供分支邏輯。

FlowSwitch < T>

根據運算式的值啟用多個分支。 

FlowStep

代表流程圖中的步驟,且能夠連線到其他步驟。 這個類型不會顯示在工具箱中,因為設計工具會隱含地新增此類型。  此活動會將其他活動包裝在工作流程中,並提供工作流程中下一個步驟 () 的導覽語意。 

請務必注意,雖然流程圖模型有特定活動,但工作流程內可以使用任何其他活動。  同樣地,流程圖活動也可以新增至另一個活動,以在該工作流程中提供流程圖的執行和設計語意。  這表示您可以有一個序列,其中包含數個活動和中間的流程圖。 

傳訊活動

WF4 的主要重點之一是 WF 與 WCF 之間的更緊密整合。  就工作流程而言,這表示建立訊息作業模型的活動,例如傳送和接收訊息。  在架構中,實際上有數個不同的活動可用於傳訊,每個活動都有稍微不同的功能和用途。 

活動 描述

傳送/接收

傳訊活動傳送或接收訊息的其中一種方式。  這些相同的活動會組成要求/回應樣式互動。 

ReceiveAndSendReply

建立服務作業的模型,以接收訊息並傳回回複。 

SendAndReceiveReply

叫用服務作業並接收回應。 

InitializeCorrelation

允許在工作流程邏輯中明確初始化相互關聯值,而不是從訊息擷取值。 

CorrelationScope

定義可存取相互關聯控制碼並傳送活動的範圍,以簡化數個傳訊活動所共用之控制碼的設定。 

TransactedReceiveScope

可讓工作流程邏輯包含在使用接收活動流入 WCF 作業的相同交易中。

若要從工作流程內叫用服務作業,您將遵循將服務參考新增至工作流程專案的熟悉步驟。  Visual Studio 中的專案系統接著會取用服務中的中繼資料,並為合約中找到的每個服務作業建立自訂活動。  您可以將這視為在 C# 或 Visual Basic 專案中建立之 WCF Proxy 的同等工作流程。   例如,使用圖 18 所示的服務合約搜尋旅館預約的現有服務;將服務參考新增至它,會產生圖 19 所示的自訂活動。 

[ServiceContract]

公用介面 IHotelService

{

    [OperationContract]

    List < HotelSearchResult > SearchHotels (

        HotelSearchRequest requestDetails) ;

}

圖 18:服務合約

圖 19:自訂 WCF 活動

如需建置公開為 WCF 服務的工作流程的詳細資訊,請參閱本文稍後的工作流程服務一節。 

交易和錯誤處理

撰寫可靠的系統可能很困難,而且您必須執行良好的處理錯誤和管理,以在應用程式中保持一致的狀態。  針對工作流程,管理例外狀況處理和交易的範圍是使用具有 ActivityAction 或 Activity 屬性的活動來建立模型,讓開發人員能夠建立錯誤處理邏輯的模型。  除了這些常見的一致性模式之外,WF4 也包含可讓您透過補償和確認來建立長時間執行分散式協調模型的活動。 

活動 描述

CancellationScope

用來允許工作流程開發人員在取消工作主體時做出反應。 

TransactionScope

藉由在交易下的範圍中執行所有子活動,啟用類似在程式碼中使用交易範圍的語意。 

TryCatch/Catch < T>

用來建立例外狀況處理的模型,並攔截具類型的例外狀況。 

Throw

可用來從活動擲回例外狀況。  

Rethrow

用來重新擲回例外狀況,通常是使用 TryCatch 活動攔截到的例外狀況。 

CompensableActivity

定義執行子活動的邏輯,這些活動可能需要在成功之後補償其動作。  提供補償邏輯、確認邏輯和取消處理的預留位置。

Compensate

叫用可補償活動的補償處理邏輯。

確認

叫用可補償活動的確認邏輯。 

TryCatch 活動提供熟悉的方式來設定一組工作範圍,以攔截可能發生的任何例外狀況,而 Catch < T > 活動會提供用於例外狀況處理邏輯的容器。  您可以在圖 20 中看到此活動的範例,其中每個型別例外狀況區塊是由 Catch < T > 活動所定義,透過每個 catch 左邊所見的具名引數提供例外狀況的存取權。   如果需要,Rethrow 活動可用來重新擲回攔截的例外狀況,或擲回活動來擲回您自己的新例外狀況。 

圖 20:TryCatch 活動

交易可協助確保短期工作的一致性,而 TransactionScope 活動提供您可以使用 TransactionScope 類別在 .NET 程式碼中取得的相同範圍範圍。 

最後,當您需要維護一致性,但無法跨資源使用不可部分完成的交易時,您可以使用補償。  補償可讓您定義一組工作,如果完成,可以有一組活動來補償所做的變更。  簡單範例中,請考慮圖 21 中傳送電子郵件的補償活動。  如果活動完成之後,就會發生例外狀況,可以叫用補償邏輯,讓系統回到一致的狀態;在此情況下,請追蹤電子郵件以撤銷先前的訊息。 

圖 21:補償活動

建立和執行工作流程

如同任何程式設計語言,您所使用的工作流程有兩個基本概念:定義工作流程,並加以執行。  在這兩種情況下,WF 都會提供數個選項來為您提供彈性和控制。 

設計工作流程的選項

設計或定義工作流程時,有兩個主要選項:程式碼或 XAML。 XAML 提供真正的宣告式體驗,並允許在 XML 標記中定義工作流程的整個定義,參考使用 .NET 建置的活動和類型。  大部分開發人員都可能會使用工作流程設計工具來建置工作流程,這會導致宣告式 XAML 工作流程定義。  不過,因為 XAML 只是 XML,所以任何工具都可以用來建立它,這是建置應用程式的強大模型之一。  例如,圖 22 中顯示的 XAML 是在簡單的文字編輯器中建立的,而且可以編譯,或直接用來執行所定義的工作流程實例。 

<p:Activity x:Class=「Workflows.HelloSeq」 xmlns=「 https://schemas.microsoft.com/netfx/2009/xaml/activities/design" ; xmlns:p=」 https://schemas.microsoft.com/netfx/2009/xaml/activities" ; xmlns:x=「 https://schemas.microsoft.com/winfx/2006/xaml" ;>

  <x:Members>

    <x:Property Name=「greeting」 Type=「p:InArgument (x:String) 」 />

    <x:Property Name=「name」 Type=「p:InArgument (x:String) 」 />

  </x:Members>

  <p:Sequence>

    <p:WriteLine > [greeting & amp;「from workflow」] < /p:WriteLine>

    <p:WriteLine > [name] < /p:WriteLine>

  </p:Sequence>

</p:Activity>

圖 22:XAML 中定義的工作流程

這個相同的 XAML 是由設計工具產生,而且可由自訂工具產生,因此更容易管理。  此外,您也可以如先前所示,使用程式碼建置工作流程。  這牽涉到透過使用架構和自訂活動中的各種活動來建立活動階層。  您已看到數個已在程式碼中完全定義的工作流程範例。  不同于 WF3,現在可以在程式碼中建立工作流程,並輕鬆地將它序列化為 XAML;在模型化和管理工作流程定義中提供更多彈性。 

如我所示,若要執行工作流程,您只需要活動,而且可以是內建程式碼的實例,或是從 XAML 建立的實例。  每個模型化技術的最終結果是衍生自 Activity 的類別,或是可還原序列化或編譯為活動的 XML 標記法。 

執行工作流程的選項

若要執行工作流程,您需要定義工作流程的活動。  有兩種典型的方式可以取得可執行檔活動:在程式碼中建立活動,或在 XAML 檔案中讀取,並將內容還原序列化為活動。  第一個選項很簡單,我已經顯示數個範例。  若要載入 XAML 檔案,您應該使用提供靜態 Load 方法的 ActivityXamlServices 類別。  只要傳入 Stream 或 XamlReader 物件,您就能回到 XAML 中代表的活動。  

一旦您擁有 Activity,最簡單的執行方式就是使用 WorkflowInvoker 類別,如同我稍早在單元測試中所做的一樣。  這個類別的 Invoke 方法具有 Activity 類型的參數,並傳回 IDictionary < 字串物件 > 。  如果您需要將引數傳遞至工作流程,請先在工作流程上定義這些引數,然後將值連同 Activity 一起傳遞至 Invoke 方法作為名稱/值組的字典。  同樣地,工作流程上定義的任何 Out 或 In/Out 引數都會傳回執行 方法的結果。  圖 23 提供從 XAML 檔案載入工作流程的範例、將引數傳遞至工作流程,以及擷取產生的輸出引數。 

活動數學WF;

使用 (Stream mathXaml = File.OpenRead (「Math.xaml」) )

{

    mathWF = ActivityXamlServices.Load (mathXaml) ;

}

var outputs = WorkflowInvoker.Invoke (mathWF,

    new Dictionary < string, object > {

    { 「operand1」, 5 },

    { 「operand2」, 10 },

    { 「operation」, 「add」 } }) ;

Assert.AreEqual < int > (15, (int) outputs[「result」], 「不正確的傳回結果」) ;

圖 23:叫用具有 in 和 out 引數的「鬆散 XAML」工作流程

請注意,在此範例中,活動是從 XAML 檔案載入,而且仍然可以接受和傳回引數。  工作流程是使用 Visual Studio 中的設計工具進行開發,方便使用,但可以在自訂設計工具中開發,並儲存在任何地方。  XAML 可以從資料庫、SharePoint 文件庫或其他存放區載入,然後再交由執行時間執行。  

使用 WorkflowInvoker 提供執行短期工作流程的最簡單機制。  它基本上讓工作流程就像應用程式中的方法呼叫一樣,讓您更輕鬆地利用 WF 的所有優點,而不需要執行許多工作來裝載 WF 本身。  這在單元測試活動與工作流程時特別有用,因為它可減少測試下練習元件所需的測試設定。 

另一個常見的裝載類別是 WorkflowApplication,可為執行時間中執行的工作流程提供安全控制碼,並可讓您更輕鬆地管理長時間執行的工作流程。  使用 WorkflowApplication,您仍然可以以與 WorkflowInvoker 相同的方式將引數傳遞至工作流程,但您可以使用 Run 方法實際啟動工作流程執行。  此時,工作流程會開始在另一個執行緒上執行,而控制項會返回呼叫端程式碼。 

由於工作流程現在以非同步方式執行,因此在裝載程式碼中,您可能會想要知道工作流程何時完成,或是否擲回例外狀況等等。針對這些類型的通知,WorkflowApplication 類別有一組動作 T > 類型的 < 屬性,可用來新增程式碼以回應工作流程執行的特定條件,包括:中止、未處理的例外狀況、已完成、閒置和卸載。  當您使用 WorkflowApplication 執行工作流程時,可以使用類似圖 24 所示的程式碼來處理事件。 

WorkflowApplication wf = 新的 WorkflowApplication (新的 Flowchart1 () ) ;

wf.Completed = 委派 (WorkflowApplicationCompletedEventArgs e)

{

    Console.WriteLine (「Workflow {0} complete」, e.InstanceId) ;

};

wf.Aborted = 委派 (WorkflowApplicationAbortedEventArgs e)

{

    Console.WriteLine (,例如原因) ;

};

wf.OnUnhandledException =

  delegate (WorkflowApplicationUnhandledExceptionEventArgs e)

  {

      Console.WriteLine (e.UnhandledException.ToString () ) ;

      return UnhandledExceptionAction.Terminate;

  };

wf.Run () ;

圖 24:WorkflowApplication 動作

在此範例中,主機程式碼的目標是簡單的主控台應用程式,是在工作流程完成時或發生錯誤時,透過主控台通知使用者。  在實際系統中,主機會對這些事件感興趣,而且可能會以不同的方式回應它們,以提供系統管理員失敗的相關資訊,或更妥善地管理實例。

工作流程延伸模組

WF 的其中一個核心功能,自 WF3 以來,它已足夠輕量型裝載在任何 .NET 應用程式域中。  因為執行時間可以在不同的網域中執行,而且可能需要自訂的執行語意,所以執行時間行為的各種層面必須從執行時間外部化。  這就是工作流程延伸模組扮演的位置。  工作流程延伸模組可讓您以開發人員的身分撰寫主機程式碼,如果您選擇將行為新增至執行時間,方法是使用自訂程式碼來擴充它。 

WF 執行時間知道的兩種擴充類型是持續性和追蹤延伸模組。 持續性延伸模組提供將工作流程狀態儲存至長期存放區的核心功能,並在需要時擷取該狀態。  架構隨附的持續性延伸模組包含 Microsoft SQL Server的支援,但延伸模組可以寫入以支援其他資料庫系統或長期存放區。 

持續性

若要使用持續性延伸模組,您必須先設定資料庫來保存狀態,這可以使用在 %windir%\Microsoft.NET\Framework\v4.0.30319\sql\ < language > (例如 c:\windows\microsoft.net\framework\v4.0.30319\sql\en\) 中找到的 SQL 腳本來完成。 建立資料庫之後,您會執行兩個腳本來建立結構 (SqlWorkflowInstanceStoreSchema.sql) 和預存程式 (SqlWorkflowInstanceStoreLogic.sql) 。 

設定資料庫之後,您可以使用 SqlWorkflowInstanceStore 以及 WorkflowApplication 類別。  您必須先建立存放區並加以設定,然後將它提供給 WorkflowApplication,如圖 25 所示。

WorkflowApplication 應用程式 = 新的 WorkflowApplication (活動) ;

InstanceStore 實例存放區 = 新的 SqlWorkflowInstanceStore (

    @"Data Source=.\\SQLEXPRESS;Integrated Security=True");

InstanceView 檢視 = instanceStore.Execute (

    instanceStore.CreateInstanceHandle () 、new CreateWorkflowOwnerCommand () 、

    TimeSpan.FromSeconds (30) ) ;

instanceStore.DefaultInstanceOwner = view。InstanceOwner;

應用。InstanceStore = instanceStore;

圖 25:新增 SQL 持續性提供者

工作流程有兩種方式可以保存。  第一個是直接使用工作流程中的保存活動。  當此活動執行時,會導致工作流程狀態保存到資料庫,並儲存工作流程的目前狀態。  這可讓工作流程作者控制何時儲存工作流程的目前狀態很重要。  第二個選項可讓主控應用程式在工作流程實例上發生各種事件時保存工作流程狀態;工作流程閒置時最有可能。  藉由在 WorkflowApplication 上註冊 PersistableIdle 巨集指令,您的主機程式碼就可以回應事件,以指出實例是否應該保存、卸載或兩者皆否。  圖 26 顯示主應用程式註冊,以在工作流程閒置時收到通知,並導致它持續存在。 

wf。PersistableIdle = () = > PersistableIdleAction.Persist;

圖 26:閒置時卸載工作流程

工作流程閒置並保存之後,WF 執行時間可以從記憶體卸載它,釋放需要處理的其他工作流程資源。  您可以選擇從 PersistableIdle 巨集指令傳回適當的列舉,讓您的工作流程實例卸載。  卸載工作流程之後,主機會想要將它載入回持續性存放區以繼續。  若要這樣做,工作流程實例必須使用實例存放區和實例識別碼來載入。 這會導致要求實例存放區載入狀態。  載入狀態之後,可以使用 WorkflowApplication 上的 Run 方法,也可以繼續書簽,如圖 27 所示。  如需書簽的詳細資訊,請參閱自訂活動一節。 

WorkflowApplication 應用程式 = 新的 WorkflowApplication (活動) ;

應用。InstanceStore = instanceStore;

應用。載入 (識別碼) ;

應用。ResumeBookmark (readLineBookmark,輸入) ;

圖 27:繼續保存的工作流程

WF4 的其中一項變更是工作流程狀態的保存方式,並高度依賴引數和變數的新資料管理技術。  只會保存範圍變數和引數值,以及書簽資訊等某些執行時間資料,而不是序列化整個活動樹狀結構並維護工作流程存留期的狀態。  請注意,圖 27 中,除了使用實例存放區之外,已重載保存的實例時,也會傳遞定義工作流程的活動。 基本上,來自資料庫的狀態會套用至所提供的活動。  這項技術可讓您更彈性地進行版本設定,並產生較佳的效能,因為狀態的記憶體使用量較小。 

追蹤

持續性可讓主機支援長時間執行的進程、跨主機的實例負載平衡,以及其他容錯行為。  不過,工作流程實例完成後,資料庫中的狀態通常會遭到刪除,因為它不再有用。  在生產環境中,具有工作流程目前正在執行之動作的相關資訊,以及其執行作業對於管理工作流程和取得商務程式見解非常重要。  能夠追蹤應用程式中發生的情況,是使用 WF 執行時間的其中一個吸引人的功能。  

追蹤包含兩個主要元件:追蹤參與者和追蹤設定檔。  追蹤設定檔會定義您想要執行時間追蹤的事件和資料。設定檔可以包含三種主要類型的查詢:

  • ActivityStateQuery – 用來識別活動狀態 (,例如關閉) 和變數或引數來擷取資料
  • WorkflowInstanceQuery – 用來識別工作流程事件
  • CustomTrackingQuery – 用來識別明確呼叫來追蹤資料,通常是在自訂活動中

圖 28 顯示正在建立的 TrackingProfile,其中包含 ActivityStateQuery 和 WorkflowInstanceQuery。   請注意,查詢會指出何時收集資訊,以及要收集的資料。  針對 ActivityStateQuery,我包含應擷取其值並新增至追蹤資料的變數清單。 

TrackingProfile 設定檔 = 新的 TrackingProfile

{

    Name = 「SimpleProfile」,

    查詢 = {

        new WorkflowInstanceQuery {

            States = { 「*」 }

        },

        new ActivityStateQuery {

            ActivityName = 「WriteLine」,

            States={ 「*」 },

            Variables = {「Text」 }

        }

    }

};

圖 28:建立追蹤設定檔

追蹤參與者是可新增至執行時間的擴充功能,負責在發出追蹤記錄時進行處理。  TrackingParticipant 基類會定義屬性,以提供 TrackingProfile 和處理追蹤的 Track 方法。  圖 29 顯示將資料寫入主控台的有限自訂追蹤參與者。  若要使用追蹤參與者,必須使用追蹤設定檔進行初始化,然後將它新增至工作流程實例上的擴充功能集合。 

public 類別 ConsoleTrackingParticipant :TrackingParticipant

{

   protected override void Track (TrackingRecord record, TimeSpan timeout)

   {

      ActivityStateRecord aRecord = record as ActivityStateRecord;

      如果 (aRecord != null)

      {

         Console.WriteLine (「 進入狀態 {1} 」 {0} ,

            aRecord.Activity.Name,aRecord.State) ;

         foreach (aRecord.Arguments 中的 var 專案)

         {

            Console.ForegroundColor = ConsoleColor.Cyan;

            Console.WriteLine (「Variable: {0} has value: {1} 」,

                專案。索引鍵、專案。值) ;

            Console.ResetColor () ;

         }

      }

   }

}

圖 29:主控台追蹤參與者

WF 隨附 EtwTrackingParticipant (ETW = 適用于 Windows 的企業追蹤) ,您可以將其新增至執行時間,以啟用資料的高效能追蹤。  ETW 是 Windows 中原生元件的追蹤系統,由作業系統中的許多元件和服務使用,包括驅動程式和其他核心層級程式碼。  寫入 ETW 的資料可以使用自訂程式碼或使用即將推出的 Windows Server AppFabric 之類的工具來取用。  對於從 WF3 移轉的使用者,這會是一項變更,因為架構中不會傳送 SQL 追蹤參與者。  不過,Windows Server AppFabric 會隨附 ETW 取用者,以收集 ETW 資料並將其儲存至 SQL 資料庫。  同樣地,您可以建置 ETW 取用者,並以您偏好的格式來儲存資料,或撰寫您自己的追蹤參與者,無論您的環境更合理。 

建立自訂活動

雖然基底活動程式庫包含與服務、物件和集合互動的豐富活動選擇,但它不提供與資料庫、電子郵件伺服器或自訂網域物件和系統等子系統互動的活動。  開始使用 WF4 的一部分是瞭解您在建置工作流程時可能需要或想要的核心活動。  好件事就是當您建置核心工作單位時,您可以將它們結合成更粗略的活動,讓開發人員可以在其工作流程中使用。 

活動類別階層

雖然活動是活動,但並非所有活動都有相同的需求或需求。  基於該理由,而不是衍生自 Activity 的所有活動,圖 30 中顯示活動基類的階層,您可以在建置自訂活動時從中選擇。 

圖 30:活動類別階層

對於大部分的自訂活動,您將衍生自 AsyncCodeActivity、CodeActivity 或 NativeActivity (或其中一個泛型變體) ,或以宣告方式建立活動模型。  概括而言,這四個基類可描述如下:

  • 活動 – 用來藉由撰寫其他活動來建立活動模型,通常是使用 XAML 所定義。
  • CodeActivity – 當您需要撰寫一些程式碼以完成工作時,簡化的基類。
  • AsyncCodeActivity - 當活動以非同步方式執行某些工作時使用。
  • NativeActivity – 當您的活動需要存取執行時間內部時,例如排程其他活動或建立書簽。

在下列各節中,我將建置數個活動,以瞭解如何使用每個基類。  一般而言,當您考慮要使用的基類時,應該從 Activity 基類開始,並查看您是否可以使用宣告式邏輯來建置活動,如下一節所示。  接下來,如果您判斷必須撰寫一些 .NET 程式碼來完成工作,而且如果您希望活動以非同步方式執行,CodeActivity 會提供簡化的模型。  最後,如果您要撰寫控制流程活動,例如架構中找到的活動 (例如 While、Switch、If) ,則您必須衍生自 NativeActivity 基類,才能管理子活動。 

使用活動設計工具撰寫活動

當您建立新的活動程式庫專案,或當您將新專案新增至 WF 專案並選取 [活動] 範本時,您會得到的是 XAML 檔案,其中含有空白 Activity 元素。  在設計工具中,這會將本身呈現為設計介面,您可以在其中建立活動的主體。  若要開始使用具有一個以上的步驟的活動,通常有助於將 Sequence 活動拖曳為 Body,然後將實際活動邏輯填入為主體代表單一子活動。 

您可以將活動想像成與使用引數之元件上的方法非常類似。  在活動本身上,您可以定義引數及其方向性,以定義活動的介面。  您想要在活動內使用的變數必須在組成主體的活動中定義,例如先前提及的根序列。  例如,您可以建置 NotifyManager 活動,以撰寫兩個更簡單的活動:GetManager 和 SendMail。 

首先,在 Visual Studio 2010 中建立新的 ActivityLibrary 專案,並將 Activity1.xaml 檔案重新命名為 NotifyManager.xaml。  接下來,從工具箱拖曳 Sequence 活動,並將其新增至設計工具。  一旦順序就緒,您就可以使用子活動填入它,如圖 31 所示。 (請注意,此範例中使用的活動是參考專案中的自訂活動,不適用於 framework.)

圖 31:通知管理員活動

此活動需要接受員工名稱和訊息本文的引數。 GetManager 活動會查閱員工的經理,並以 OutArgument < 字串 > 的形式提供電子郵件。  最後,SendMail 活動會將訊息傳送給管理員。  下一個步驟是展開設計工具底部的 Arguments 視窗,並輸入兩個必要輸入引數的資訊,以定義活動的引數。 

若要撰寫這些專案,您必須能夠將 NotifyManager 活動上指定的輸入引數傳遞至個別子活動。  針對 GetManager 活動,您需要員工名稱,這是活動的引數。 您可以在 GetManager 活動的 employeeName 引數屬性對話方塊中使用引數名稱,如圖 31 所示。  SendMail 活動需要管理員的電子郵件地址和訊息。  針對訊息,您可以輸入包含訊息的引數名稱。  不過,對於電子郵件地址,您需要一種方式,將 out 引數從 GetManager 活動傳遞至 SendMail 活動的 in 引數。  為此,您需要變數。 

醒目提示時序活動之後,您可以使用 [變數] 視窗來定義名為 「mgrEmail」 的變數。  現在您可以在 GetManager 活動上輸入 ManagerEmail out 引數的變數名稱,以及 SendMail 活動的 To in 引數。  當 GetManager 活動執行時,輸出資料會儲存在該變數中,而 SendMail 活動執行時,它會從該變數讀取資料作為 in 引數。 

剛才描述的方法是定義活動主體的純宣告式模型。  在某些情況下,您可能會偏好在程式碼中指定活動的主體。  例如,您的活動可能包含屬性集合,進而驅動一組子活動;一組具名值,可驅動建立一組 Assign 活動,就是使用程式碼的一種情況。  在這些情況下,您可以撰寫衍生自活動的類別,並在實作屬性中撰寫程式碼,以建立 Activity (和任何子專案) 來代表活動的功能。  在這兩種情況下,最終結果都相同:您的邏輯是藉由撰寫其他活動來定義,而只有定義本文的機制不同。  圖 32 顯示程式碼中定義的相同 NotifyManager 活動。 

public 類別 NotifyManager : Activity

{

    public InArgument < string > EmployeeName { get; set; }

    public InArgument < 字串 > Message { get; set; }

    protected 覆寫 Func < 活動 > 實作

    {

        get

        {

            return () =>

            {

                變數 < 字串 > mgrEmail =

                new Variable < string > { Name = 「mgrEmail」 };

                序列 s = 新序列

                {

                    變數 = { mgrEmail },

                    活動 = {

                        new GetManager {

                            EmployeeName =

                                新的 VisualBasicValue < 字串 > (「EmployeeName」) ,

                                Result = mgrEmail,

                        },

                        new SendMail {

                            ToAddress = mgrEmail,

                            MailBody = 新的 VisualBasicValue < 字串 > (「Message」) ,

                            From = 「 test@contoso.com 」,

                            主旨 = 「自動化電子郵件」

                        }

                    }

                };

                傳回 s;

            };

        }

        set { base.實作 = 值;}

    }

}

圖 32:使用程式碼的活動組合

請注意,基類是 Activity,而實作屬性是用來傳回單一 Activity 的 Func < Activity > 。 此程式碼與稍早建立工作流程時所示的程式碼非常類似,而且這兩種情況下的目標都不是建立活動。  此外,您可以在程式碼方法引數中使用變數來設定,或者您可以使用運算式將一個引數連接到另一個引數,如 EmployeeName 和 Message 引數在兩個子活動上使用時所示。 

撰寫自訂活動類別

如果您決定無法藉由撰寫其他活動來完成活動邏輯,則可以撰寫程式碼型活動。  在程式碼中撰寫活動時,您會衍生自適當的類別、定義引數,然後覆寫 Execute 方法。  執行時間會在活動執行其工作時呼叫 Execute 方法。

若要建置上一個範例中使用的 SendMail 活動,我必須先選擇基底類型。  雖然您可以使用 Activity 基類和撰寫 TryCatch < T > 和 InvokeMethod 活動來建立 SendMail 活動的功能,但對於大部分的開發人員而言,選擇 CodeActivity 和 NativeActivity 基類並撰寫執行邏輯的程式碼會比較自然。  由於此活動不需要建立書簽或排程其他活動,因此我可以衍生自 CodeActivity 基類。  此外,由於此活動會傳回單一輸出引數,因此它應該衍生自 CodeActivity < TResult > ,以提供 OutArgument < TResult > 。  第一個步驟是定義電子郵件屬性的數個輸入引數。  然後,您可以覆寫 Execute 方法以實作活動的功能。  圖 33 顯示已完成的活動類別。 

public 類別 SendMail :CodeActivity < SmtpStatusCode>

{

    public InArgument < string > To { get; set; }

    public InArgument < string > From { get; set; }

    public InArgument < 字串 > Subject { get; set; }

    public InArgument < 字串 > Body { get; set; }

    protected override SmtpStatusCode Execute (

    CodeActivityCoNtext 內容)

    {

        嘗試

        {

            SmtpClient 用戶端 = 新的 SmtpClient () ;

            客戶。傳送 (

            From.Get (coNtext) , ToAddress.Get (coNtext) ,

            Subject.Get (coNtext) , MailBody.Get (coNtext) ) ;

        }

        catch (SmtpException smtpEx)

        {

            return smtpEx.StatusCode;

        }

        return SmtpStatusCode.Ok;

    }

}

圖 33:SendMail 自訂活動

有數件事需要注意有關使用引數。  我已使用 InArgument < T > 類型宣告數個標準 .NET 屬性。  這是以程式碼為基礎的活動定義其輸入和輸出引數的方式。  不過,在程式碼中,我不能直接參考這些屬性來取得引數的值,我需要使用 引數做為控制碼,才能使用提供的 ActivityCoNtext 來擷取值;在此案例中為 CodeActivityCoNtext。  您可以使用引數類別的 Get 方法來擷取值,並使用 Set 方法將值指派給引數。 

一旦活動完成 Execute 方法,執行時間就會偵測到這個,並假設活動已完成並關閉。  對於非同步或長時間執行的工作,有方法可以變更即將推出的章節中說明的這個行為,但在此案例中,一旦傳送電子郵件,工作就會完成。 

現在讓我們使用 NativeActivity 基類來檢查活動,以管理子活動。  我將會建置反覆運算器活動,以指定次數執行某些子活動。  首先,我需要引數來保存要執行的反復專案數目、要執行的 Activity 屬性,以及保存目前反復專案的變數。 Execute 方法會呼叫 BeginIteration 方法來啟動初始反復專案,而後續反復專案會以相同方式叫用。  請注意,圖 34 中的變數會以與使用 Get 和 Set 方法的引數相同的方式來操作。 

公用類別 Iterator :NativeActivity

{

    public Activity Body { get; set; }

    public InArgument < int > RequestedIterations { get; set; }

    public Variable < int > CurrentIteration { get; set; }

    public Iterator ()

    {

        CurrentIteration = new Variable < int > { Default = 0 };

    }

    protected override void Execute (NativeActivityCoNtext coNtext)

    {

        BeginIteration (內容) ;

    }

    private void BeginIteration (NativeActivityCoNtext 內容)

    {

        如果 (RequestedIterations.Get (coNtext) > CurrentIteration.Get (coNtext) )

        {

            上下文。ScheduleActivity (Body、

            new CompletionCallback (ChildComplete) ,

            new FaultCallback (ChildFaulted) ) ;

        }

    }

}

圖 34:反覆運算器原生活動

如果您仔細查看 BeginIteration 方法,您會發現 ScheduleActivity 方法呼叫。  這是活動如何與執行時間互動,以排程執行的另一個活動,這是因為您需要衍生自 NativeActivity 的這項功能。  另請注意,在 ActivityExecutionCoNtext 上呼叫 ScheduleActivity 方法時,會提供兩個回呼方法。  因為您不知道要使用哪一個活動做為主體,或需要多久時間才能完成,而且因為 WF 是高度非同步程式設計環境,所以回呼是用來在子活動完成時通知您的活動,讓您撰寫程式碼以回應。

第二個回呼是 FaultCallback,如果子活動發生擲回例外狀況,就會叫用此回呼。  在此回呼中,您會收到例外狀況,並能夠透過 ActivityFaultCoNtext 向執行時間指出已處理錯誤,這樣會讓它無法向上和移出活動。 圖 35 顯示 Iterator 活動的回呼方法,其中同時排程下一個反復專案和 FaultCallback 會處理錯誤,以允許執行繼續下一個反復專案。

private void ChildComplete (NativeActivityCoNtext 內容,

ActivityInstance 實例)

{

    CurrentIteration.Set (coNtext, CurrentIteration.Get (coNtext) + 1) ;

    BeginIteration (內容) ;

}

private void ChildFaulted (NativeActivityFaultCoNtext 內容,例外狀況例如

ActivityInstance 實例)

{

    CurrentIteration.Set (coNtext, CurrentIteration.Get (coNtext) + 1) ;

    上下文。HandleFault () ;

}

圖 35:活動回呼

當您的活動需要執行可能長時間執行的工作時,最好將工作交給另一個執行緒,以允許工作流程執行緒用來繼續處理其他活動,或用來處理其他工作流程。  不過,只要啟動執行緒並將控制權傳回給執行時間可能會導致問題,因為執行時間不會監視您所建立的執行緒。  如果執行時間判斷工作流程閒置,工作流程可能會卸載、處置回呼方法,或者執行時間可能會假設您的活動已完成,並移至工作流程中的下一個活動。  若要在您的活動中支援非同步程式設計,請衍生自 AsyncCodeActivity 或 AsyncCodeActivity < T > 。 

圖 36 顯示載入 RSS 摘要的非同步活動範例。  執行方法的這個簽章與非同步活動不同,因為它會傳回 IAsyncResult。  您可以在 execute 方法中撰寫程式碼,以開始非同步作業。  覆寫 EndExecute 方法,以在非同步作業完成時處理回呼,並傳回執行的結果。 

public sealed 類別 GetFeed:AsyncCodeActivity < SyndicationFeed>

{

    public InArgument < Uri > FeedUrl { get; set; }

    protected override IAsyncResult BeginExecute (

    AsyncCodeActivityCoNtext 內容、AsyncCallback 回呼、

    物件狀態)

    {

        var req = (HttpWebRequest) HttpWebRequest.Create (

        FeedUrl.Get (coNtext) ) ;

        req.方法 = 「GET」;

        上下文。UserState = req;

        return req。BeginGetResponse (新的 AsyncCallback (回呼) 狀態) ;

    }

    protected override SyndicationFeed EndExecute (

    AsyncCodeActivityCoNtext 內容、IAsyncResult 結果)

    {

        HttpWebRequest req = coNtext。UserState 做為 HttpWebRequest;

        WebResponse wr = req。EndGetResponse (結果) ;

        SyndicationFeed localFeed = SyndicationFeed.Load (

        XmlReader.Create (wr.GetResponseStream () ) ) ;

        return localFeed;

    }

}

圖 36:建立異步活動

其他活動概念

現在您已瞭解使用基類、引數和變數建立活動的基本概念,以及管理執行;我會簡短地瞭解一些可在活動開發中使用的更進階功能。 

書簽可讓活動作者在執行工作流程時建立繼續點。  建立書簽之後,就可以繼續繼續工作流程處理,從其離開的位置繼續進行工作流程處理。  書簽會儲存為狀態的一部分,因此與非同步活動不同,在建立書簽之後,工作流程實例不會保存和卸載。  當主機想要繼續工作流程時,它會載入工作流程實例,並呼叫 ResumeBookmark 方法,從其離開之處繼續實例。  繼續書簽時,資料也可以傳遞至活動。   圖 37 顯示 ReadLine 活動,其會建立書簽以接收輸入,並在資料送達時註冊要叫用的回呼方法。  執行時間知道活動何時有未完成的書簽,而且在書簽繼續之前不會關閉活動。  ResumeBookmark 方法可用於 WorkflowApplication 類別,將資料傳送至具名書簽,併發出 BookmarkCallback 的訊號。  

public 類別 ReadLine :NativeActivity < 字串>

{

    public OutArgument < string > InputText { get; set; }

    protected override void Execute (NativeActivityCoNtext 內容)

    {

        上下文。CreateBookmark (「ReadLine」,

        新的 BookmarkCallback (BookmarkResumed) ) ;

    }

    private void BookmarkResumed (NativeActivityCoNtext 內容,

        書簽 bk,物件狀態)

    {

        Result.Set (內容、狀態) ;

    }

}

圖 37:建立書簽

活動作者的另一個強大功能是 ActivityAction 的概念。  ActivityAction 是命令式程式碼中 Action 類別的對等工作流程:描述通用委派;但對於某些專案而言,範本的概念可能更容易瞭解。  請考慮 ForEach < T > 活動,可讓您逐一查看一組資料,並排程每個資料項目的子活動。  您需要一些方法來允許活動的取用者定義主體,並能夠取用類型 T 的資料項目。基本上,您需要一些可以接受 T 類型的專案並對其採取動作的活動,ActivityAction < T > 是用來啟用此案例,並提供引數的參考和要處理該專案的活動定義。  您可以在自訂活動中使用 ActivityAction,讓活動的取用者能夠在適當的時間點新增自己的步驟。  這可讓您建立排序的工作流程或活動範本,其中取用者可以填入相關的元件,以自訂其使用的執行。  當委派活動需要叫用時,您也可以使用 ActivityFunc < TResult > 和相關替代方案,將傳回值。  

最後,可以驗證活動,以確保它們已正確設定,方法是檢查個別設定、限制允許的子活動等等。針對需要特定引數的常見需求,您可以將 RequiredArgument 屬性新增至活動中的屬性宣告。  如需更相關的驗證,請在活動的建構函式中建立條件約束,並將它新增至活動類別上顯示的條件約束集合。  條件約束是寫入以檢查目標活動並確保有效性的活動。  圖 38 顯示 Iterator 活動的建構函式,其會驗證 RequestedIterations 屬性是否已設定。 最後,覆寫 CacheMetadata 方法,您可以在 ActivityMetdata 參數上叫用 AddValidationError 方法,以新增活動的驗證錯誤。 

var act = new DelegateInArgument < Iterator > { Name = 「constraintArg」 };

var vctx = new DelegateInArgument < ValidationCoNtext > () ;

條件約束 < 反覆運算器 > cons = 新的條件約束 < 反覆運算器>

{

    Body = new ActivityAction < Iterator, ValidationCoNtext>

    {

        Argument1 = act,

        Argument2 = vctx,

        Handler = new AssertValidation

        {

            訊息 = 「必須提供反復專案」,

            PropertyName = 「RequestedIterations」,

            判斷提示 = 新的 InArgument < bool > (

                (e) = > act。取得 e) (。RequestedIterations != null)

        }

    }

};

基地。Constraints.Add (cons) ;

圖 38:條件約束

活動設計工具

WF 的其中一個優點是,它可讓您以宣告方式設計應用程式邏輯,但大部分的人都不想手動撰寫 XAML,這就是為什麼 WF 中的設計工具體驗非常重要。  建置自訂活動時,您可能也想要建立設計工具,為活動的取用者提供顯示和視覺互動體驗。  WF 的設計工具是以Windows Presentation Foundation為基礎,這表示您擁有所有樣式、觸發程式、資料系結,以及為設計工具建置豐富 UI 的其他所有絕佳工具。  此外,WF 提供一些使用者控制項,可讓您在設計工具中用來簡化顯示個別子活動或活動集合的工作。  四個主要控制項包括:

  • ActivityDesigner – 活動設計工具中使用的根 WPF 控制項
  • WorkflowItemPresenter – 用來顯示單一活動
  • WorkflowItemsPresenter – 用來顯示子活動的集合
  • ExpressionTextBox – 用來就地編輯運算式,例如引數

WF4 包含 ActivityDesignerLibrary 專案範本和 ActivityDesigner 專案範本,可用來一開始建立成品。  初始化設計工具之後,您可以藉由配置如何顯示子活動的資料和視覺化表示,開始使用上述元素來自訂活動的外觀和風格。  在設計工具中,您可以存取 ModelItem,這是實際活動的抽象概念,並呈現活動的所有屬性。 

WorkflowItemPresenter 控制項可用來顯示屬於您活動的單一活動。  例如,若要提供將活動拖曳到稍早顯示的 Iterator 活動,並顯示 Iteractor 活動中包含的活動的能力,您可以使用圖 39 所示的 XAML,將 WorkflowItemPresenter 系結至 ModelItem.Body。  圖 40 顯示工作流程設計介面上使用的設計工具。 

<sap:ActivityDesigner x:Class=「CustomActivities.Presentation.IteratorDesigner」

  xmlns=「 https://schemas.microsoft.com/winfx/2006/xaml/presentation" ;

  xmlns:x=「 https://schemas.microsoft.com/winfx/2006/xaml" ;

  xmlns:sap=「clr-namespace:System.Activities.Presentation;

assembly=System.Activities.Presentation」

  xmlns:sapv=「clr-namespace:System.Activities.Presentation.View;

assembly=System.Activities.Presentation」>

  <格線>

    <Border BorderBrush=「MidnightBlue」 BorderThickness=「3」 CornerRadius=「10」>

<sap:WorkflowItemPresenter MinHeight=「50」

Item=「{Binding Path=ModelItem.Body, Mode=TwoWay}」

HintText=「在這裡新增本文」 />

    </邊境>

  </網 格>

</sap:ActivityDesigner>

圖 39:活動設計工具中的 WorkflowItemPresenter

圖 40:反覆運算器設計工具

WorkflowItemsPresenter 提供類似的功能來顯示多個專案,例如序列中的子系。 您可以定義要如何顯示專案的範本和方向,以及空白字元範本,以在連接器、箭號或更有趣的活動之間新增視覺元素。  圖 41 顯示自訂順序活動的簡單設計工具,其中顯示每個活動之間具有水平線的子活動。

<sap:ActivityDesigner x:Class=「CustomActivities.Presentation.CustomSequenceDesigner」

    xmlns=「 https://schemas.microsoft.com/winfx/2006/xaml/presentation" ;

    xmlns:x=「 https://schemas.microsoft.com/winfx/2006/xaml" ;

    xmlns:sap=「clr-namespace:System.Activities.Presentation;

assembly=System.Activities.Presentation」

    xmlns:sapv=「clr-namespace:System.Activities.Presentation.View;

assembly=System.Activities.Presentation」>

    <格線>

      <StackPanel>

         <Border BorderBrush=「Goldenrod」 BorderThickness=「3」>

     <sap:WorkflowItemsPresenter HintText=「Drop Activities here」

  Items=「{Binding Path=ModelItem.ChildActivities}」>

     <sap:WorkflowItemsPresenter.SpacerTemplate>

  <DataTemplate>

    <矩形高度=「3」 Width=「40」

 Fill=「MidnightBlue」 Margin=「5」 />

                     </DataTemplate>

                  </sap:WorkflowItemsPresenter.SpacerTemplate>

                  <sap:WorkflowItemsPresenter.ItemsPanel>

                      <ItemsPanelTemplate>

                          <StackPanel Orientation=「Vertical」/>

                      </ItemsPanelTemplate>

                   </sap:WorkflowItemsPresenter.ItemsPanel>

            </sap:WorkflowItemsPresenter>

        </邊境>

      </StackPanel>

    </網 格>

</sap:ActivityDesigner>

圖 41:使用 WorkflowItemsPresenter 的自訂順序設計工具

圖 42:順序設計工具

最後,ExpressionTextBox 控制項除了屬性方格之外,還可在設計工具內就地編輯活動引數。 在 Visual Studio 2010 中,這包括輸入之運算式的 Intellisense 支援。 圖 43 顯示稍早建立之 GetManager 活動的設計工具,可編輯設計工具內的 EmployeeName 和 ManagerEmail 引數。 實際設計工具如圖 44 所示。 

<sap:ActivityDesigner x:Class=「CustomActivities.Presentation.GetManagerDesigner」

    xmlns=「 https://schemas.microsoft.com/winfx/2006/xaml/presentation" ;

    xmlns:x=「 https://schemas.microsoft.com/winfx/2006/xaml" ;

    xmlns:sap=「clr-namespace:System.Activities.Presentation;

assembly=System.Activities.Presentation」

    xmlns:sapv=「clr-namespace:System.Activities.Presentation.View;

assembly=System.Activities.Presentation」

xmlns:sapc=「clr-namespace:System.Activities.Presentation.Converters;

assembly=System.Activities.Presentation」>

    <sap:ActivityDesigner.Resources>

        <sapc:ArgumentToExpressionConverter

x:Key=「ArgumentToExpressionConverter」

x:Uid=「swdv:ArgumentToExpressionConverter_1」 />

    </sap:ActivityDesigner.Resources>

    <格線>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width=「50」 />

             <ColumnDefinition Width=「*」 />

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition />

            <RowDefinition />

            <RowDefinition />

        </Grid.RowDefinitions>

        <TextBlock VerticalAlignment=「Center」

 HorizontalAlignment=「Center」 > Employee: < /TextBlock>

        <sapv:ExpressionTextBox Grid.Column=「1」

              Expression=「{Binding Path=ModelItem.EmployeeName, Mode=TwoWay,

       Converter={StaticResource ArgumentToExpressionConverter},

       ConverterParameter=In}「 OwnerActivity=」{Binding Path=ModelItem}」

MinLines=「1」 MaxLines=「1」 MinWidth=「50」

HintText=「 & lt;員工名稱 & gt;」/>

        <TextBlock Grid.Row=「1」 VerticalAlignment=「Center」

              HorizontalAlignment=「Center」 > Mgr Email: < /TextBlock>

        <sapv:ExpressionTextBox Grid.Column=「2」 Grid.Row=「1」

    UseLocationExpression=「True」

           Expression=「{Binding Path=ModelItem.ManagerEmail, Mode=TwoWay,

    Converter={StaticResource ArgumentToExpressionConverter},

    ConverterParameter=Out}「 OwnerActivity=」{Binding Path=ModelItem}」

    MinLines=「1」 MaxLines=「1」 MinWidth=「50」 />

    </網 格>

</sap:ActivityDesigner>

圖 43:使用 ExpressionTextBox 編輯設計工具中的引數

圖 44:GetManager 活動設計工具

此處所呈現的範例設計工具很容易醒目提示特定功能,但 WPF 的完整功能可供您使用,讓您的活動更容易設定,並更自然地用於取用者。   建立設計工具之後,有兩種方式可將它與活動產生關聯:將屬性套用至活動類別,或實作 IRegisterMetadata 介面。  若要讓活動定義驅動設計工具的選擇,只要將 DesignerAttribute 套用至活動類別並提供設計工具的類型即可;這與 WF3 中的運作方式完全相同。  如需更鬆散結合的實作,您可以實作 IRegisterMetadata 介面,並定義您想要套用至活動類別和屬性的屬性。  圖 45 顯示範例實作,可套用兩個自訂活動的設計工具。 Visual Studio 會探索您的實作並自動叫用它,而接下來討論的自訂設計工具主機會明確呼叫 方法。

public 類別中繼資料:IRegisterMetadata

{

    public void Register ()

    {

        AttributeTableBuilder builder = new AttributeTableBuilder () ;

        建設者。AddCustomAttributes (typeof (SendMail) , new Attribute[] {

            new DesignerAttribute (typeof (SendMailDesigner) ) }) ;

        建設者。AddCustomAttributes (typeof (GetManager) , new Attribute[]{

            new DesignerAttribute (typeof (GetManagerDesigner) ) }) ;

        MetadataStore.AddAttributeTable (builder。CreateTable () ) ;

    }

}

圖 45:為活動註冊設計工具

重新裝載工作流程設計工具

在過去,開發人員通常想要讓使用者能夠建立或編輯工作流程。  在舊版的 Windows Workflow Foundation 中,這是可行的,但不是簡單的工作。  移至 WPF 是新的設計工具,而重新裝載是開發小組的主要使用案例。  您可以在自訂 WPF 應用程式中裝載設計工具,如圖 46 所示,其中包含幾行 XAML 和幾行程式碼。 

圖 46:重新裝載的工作流程設計工具

圖 47 視窗的 XAML 會使用方格來配置三個數據行,然後在每個儲存格中放置框線控制項。  在第一個資料格中,工具箱是以宣告方式建立,但也可以在程式碼中建立。  針對設計工具檢視本身和屬性方格,每個其餘儲存格中都有兩個空白框線控制項。  這些控制項會在程式碼中新增。 

<Window x:Class=「WorkflowEditor.MainWindow」

        xmlns=「 https://schemas.microsoft.com/winfx/2006/xaml/presentation" ;

        xmlns:x=「 https://schemas.microsoft.com/winfx/2006/xaml" ;

        xmlns:sys=「clr-namespace:System;assembly=mscorlib」

 xmlns:sapt=「clr-namespace:System.Activities.Presentation.Toolbox;

assembly=System.Activities.Presentation」

        Title=「Workflow Editor」 Height=「500」 Width=「700」 >

    <Window.Resources>

        <sys:String x:Key=「AssemblyName」 > System.Activities, Version=4.0.0.0,

Culture=neutral, PublicKeyToken=31bf3856ad364e35 < /sys:String>

        <sys:String x:Key=「MyAssemblyName」 > CustomActivities < /sys:String>

    </Window.Resources>

    <Grid x:Name=「DesignerGrid」>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width=「2*」 />

            <ColumnDefinition Width=「7*」 />

            <ColumnDefinition Width=「3*」 />

        </Grid.ColumnDefinitions>

        <Border>

            <sapt:ToolboxControl>

                <sapt:ToolboxControl.Categories>

                    <sapt:ToolboxCategory CategoryName=「Basic」>

                        <sapt:ToolboxItemWrapper

  AssemblyName=「{StaticResource AssemblyName}」 >

                            <sapt:ToolboxItemWrapper.ToolName>

                                System.Activities.Statements.Sequence

                            </sapt:ToolboxItemWrapper.ToolName>

                        </sapt:ToolboxItemWrapper>

                        <sapt:ToolboxItemWrapper

AssemblyName=「{StaticResource MyAssemblyName}」>

                            <sapt:ToolboxItemWrapper.ToolName>

                                CustomActivities.SendMail

                             </sapt:ToolboxItemWrapper.ToolName>

                        </sapt:ToolboxItemWrapper>

                    </sapt:ToolboxCategory>

                </sapt:ToolboxControl.Categories>

            </sapt:ToolboxControl>

        </邊境>

        <框線名稱=「DesignerBorder」 Grid.Column=「1」

BorderBrush=「一元」 BorderThickness=「3」 />

        <框線 x:Name=「PropertyGridExpander」 Grid.Column=「2」 />

    </網 格>

</視窗>

圖 47:Designer重新裝載 XAML

初始化設計工具與屬性方格的程式碼會顯示在圖 48 中。  OnInitialized 方法中的第一個步驟是針對工作流程設計工具中使用的所有活動註冊設計工具。  DesignerMetadata 類別會為隨附于架構的活動註冊相關聯的設計工具,而 Metadata 類別會為我的自訂活動註冊設計工具。  接下來,建立 WorkflowDesigner 類別,並使用 View 和 PropertyInspectorView 屬性來存取轉譯所需的 UIElement 物件。  設計工具會使用空的 Sequence 初始化,但您也可以新增功能表,並從各種來源載入工作流程 XAML,包括檔案系統或資料庫。 

public MainWindow ()

{

    InitializeComponent () ;

}

protected override void OnInitialized (EventArgs e) {

    基地。OnInitialized (e) ;

    RegisterDesigners () ;

    PlaceDesignerAndPropGrid () ;

}

private void RegisterDesigners () {

    new DesignerMetadata () 。Register () ;

    new CustomActivities.Presentation.Metadata () 。Register () ;

}

private void PlaceDesignerAndPropGrid () {

    WorkflowDesigner 設計工具 = 新的 WorkflowDesigner () ;

    設計師。載入 (新的 System.Activities.Statements.Sequence () ) ;

    DesignerBorder.Child = designer。視圖;

    PropertyGridExpander.Child = 設計工具。PropertyInspectorView;

}

圖 48:Designer重新裝載程式碼

透過這個少量的程式碼和標記,您可以從工具箱編輯工作流程、拖放活動、在工作流程中設定變數,以及操作活動的引數。  您也可以在設計工具上呼叫 Flush 來取得 XAML 的文字,然後參考 WorkflowDesigner 的 Text 屬性。  您可以執行更多工作,而且開始使用比之前更容易。 

工作流程服務

如先前所述,此 Windows Workflow Foundation 版本中的其中一大重點是與 Windows Communication Foundation 緊密整合。  活動逐步解說中的範例示範如何從工作流程呼叫服務,而本節將討論如何將工作流程公開為 WCF 服務。 

若要開始,請建立新的專案,然後選取 WCF 工作流程服務專案。 範本完成後,您將找到具有web.config檔案的專案,以及具有 XAMLX 副檔名的檔案。  XAMLX 檔案是宣告式服務或工作流程服務,與其他 WCF 範本一樣,提供可正常運作的服務實作,以接收要求並傳迴響應。  在此範例中,我將會變更合約來建立作業以接收費用報表,並傳回已收到報表的指標。  若要開始,請將 ExpenseReport 類別新增至專案,就像圖 49 所示的專案一樣。

[DataContract]

public 類別 ExpenseReport

{

    [DataMember]

    public DateTime FirstDateOfTravel { get; set; }

    [DataMember]

    public double TotalAmount { get; set; }

    [DataMember]

    public string EmployeeName { get; set; }

}

圖 49:費用報表合約類型

回到工作流程設計工具,將變數中的 「data」 變數類型變更為剛才定義的 ExpenseReport 類型 (您可能需要建置專案,才能在類型選擇器對話方塊中顯示) 。 按一下 [內容] 按鈕,並將對話方塊中的類型變更為 ExpenseReport 類型以符合,以更新 [接收] 活動。  更新活動屬性以定義新的 OperationName 和 ServiceContractName,如圖 50 所示。 

圖 50:設定接收位置

現在 SendReply 活動可以將 Value 設定為 True,以傳回收據的指標。  顯然,在更複雜的範例中,您可以使用工作流程的完整功能,將資訊儲存至資料庫、執行報表驗證等等。判斷回應之前。 使用 WCFTestClient 應用程式流覽至服務會顯示活動組態如何定義公開的服務合約,如圖 51 所示。 

圖 51:從工作流程服務產生的合約

建立工作流程服務之後,您需要裝載它,就像使用任何 WCF 服務一樣。  上一個範例使用最簡單的選項來裝載:IIS。  若要在您自己的進程中裝載工作流程服務,您會想要使用 System.ServiceModel.Activities 元件中找到的 WorkflowServiceHost 類別。  請注意,System.WorkflowServices 元件中有另一個相同名稱的類別,用於裝載使用 WF3 活動所建置的工作流程服務。  在最簡單的自我裝載案例中,您可以使用如圖 52 所示的程式碼來裝載您的服務並新增端點。 

WorkflowServiceHost 主機 = new

    WorkflowServiceHost (XamlServices.Load (「ExpenseReportService.xamlx」) ,

    新 URI (「 https://localhost:9897/Services/Expense" ;) ) ;

主機。AddDefaultEndpoints () ;

主機。Description.Behaviors.Add (

    new ServiceMetadataBehavior { HttpGetEnabled = true }) ;

主機。Open () ;

Console.WriteLine (「主機已開啟」) ;

Console.ReadLine();

圖 52:自我裝載工作流程服務

如果您想要利用 Windows 中的受控裝載選項,您可以將 XAMLX 檔案複製到目錄中,並指定行為、系結、端點等任何必要組態資訊,以在 IIS 中裝載服務。在 web.config 檔案中。  事實上,如先前所見,當您使用其中一個宣告式工作流程服務專案範本在 Visual Studio 中建立新專案時,您會取得具有web.config檔案的專案,以及 XAMLX 中定義的工作流程服務,準備好進行部署。 

使用 WorkflowServiceHost 類別來裝載工作流程服務時,您仍然需要能夠設定和控制工作流程實例。  若要控制服務,有一個名為 WorkflowControlEndpoint 的新標準端點。  WorkflowControlClient 隨附類別提供預先建置的用戶端 Proxy 。  您可以在服務上公開 WorkFlowControlEndpoint,並使用 WorkflowControlClient 來建立和執行工作流程的新實例,或控制執行中的工作流程。  您可以使用這個相同的模型來管理本機電腦上的服務,或者您可以使用它來管理遠端電腦上的服務。  圖 53 顯示在服務上公開工作流程式控制制端點的更新範例。 

WorkflowServiceHost 主機 = new

    WorkflowServiceHost (「ExpenseReportService.xamlx」,

    新 URI (「 https://localhost:9897/Services/Expense" ;) ) ;

主機。AddDefaultEndpoints () ;

WorkflowControlEndpoint wce = 新的 WorkflowControlEndpoint (

    new NetNamedPipeBinding () ,

    new EndpointAddress (「net.pipe://localhost/Expense/WCE」) ) ;

主機。AddServiceEndpoint (wce) ;

主機。Open () ;

圖 53:工作流程式控制制端點

因為您不是直接建立 WorkflowInstance,所以有兩個選項可用來將擴充功能新增至執行時間。  第一個是您可以將一些擴充功能新增至 WorkflowServiceHost,而且它們會使用您的工作流程實例正確初始化。  第二個是使用組態。  例如,追蹤系統可以在應用程式組態檔中完全定義。   您可以在組態檔中定義追蹤參與者和追蹤設定檔,並使用行為,將特定參與者套用至服務,如圖 54 所示。 

<system.serviceModel>

    <tracking>

      <participants>

        <add name=「EtwTrackingParticipant」

             type=「System.Activities.Tracking.EtwTrackingParticipant, System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35」

             profileName=「HealthMonitoring_Tracking_Profile」/>

      </參與者>

      <設定檔>

        <trackingProfile name=「HealthMonitoring_Tracking_Profile」>

          <workflow activityDefinitionId=「*」>

            <workflowInstanceQuery>

              <states>

                <state name=「Started」/>

                <state name=「Completed」/>

              </國家>

            </workflowInstanceQuery>

          </流程>

        </trackingProfile>

      </配置 檔>

</跟蹤>

. . .

<behaviors>

      <serviceBehaviors>

        <行為名稱=「SampleTrackingSample.SampleWFBehavior」>

          <etwTracking profileName=「 HealthMonitoring_Tracking_Profile」 />

        </行為>

      </serviceBehaviors>

</行為>

. . .

圖 54:設定服務的追蹤

針對 WCF 服務的裝載和設定有許多新的改善,您可以在工作流程中利用這些服務,例如 「.svc-less」 服務,或不需要副檔名、預設系結和預設端點的服務來命名一些。  如需 WCF4 中這些功能和其他功能的詳細資訊,請參閱其他資源一節中找到的 WCF 隨附檔。 

除了裝載改善之外,Windows Workflow Foundation 還利用 WCF 中的其他功能;一些直接和其他人間接。  例如,訊息相互關聯現在是建置服務的重要功能,可讓您根據訂單號碼或員工識別碼等訊息中的資料,將訊息與指定的工作流程實例產生關聯。您可以在要求和回應的各種傳訊活動上設定相互關聯,並支援在訊息中的多個值上相互關聯。  同樣地,您可以在 WCF4 的隨附檔中找到這些新功能的詳細資料。   

結論

隨附于 .NET Framework 4 的新 Windows Workflow Foundation 技術代表效能和開發人員生產力的重大改善,並實現商務邏輯的宣告式應用程式開發目標。  此架構提供簡化簡短複雜工作單位的工具,以及透過追蹤建置具有相互關聯訊息、保存狀態和豐富應用程式可見度的複雜長時間執行服務。 

關於作者

Matt 是 Pluralsight 的技術人員成員,他著重于 WCF、WF、BizTalk、AppFabric 和 Azure 服務平臺) 的連線系統 (技術。 Matt 也是 Microsoft .NET 應用程式設計和開發的獨立顧問。 身為作者 Matt 參與數個日誌和雜誌,包括 MSDN Magazine,他目前撰寫 Foundations 資料行的工作流程內容。 Matt 定期分享他對於科技的愛,方法是在技術等當地、地區和國際研討會進行演講。 Microsoft 已將 Matt 辨識為 MVP,其社群對於已連線系統技術的貢獻。 透過他的部落格連絡 Matt: http://www.pluralsight.com/community/blogs/matt/default.aspx

其他資源