學習完本章,你將掌握:
1.workflow活動是怎樣形成的
2.順序工作流和狀態機工作流之間的區別
3.創建一個順序工作流
4.創建一個狀態機工作流
活動是WF中workflow處理的基本單位,假如你再把一個業務處理過程(或workflow任務)進行分解,你會發現它由更小、更細的任務組成。假如需設計這樣一個大的任務,它需把信息送到一系列的數據處理系統進行處理,那麼子任務或許就包括這樣一些事情:從數據庫讀取數據,使用這些數據生成一個文件,通過使用FTP或XML Web service把文件傳到一個遠程服務器上,標記信息已經被處理(如通過寫入數據庫並進入審計步驟),等等。這些子任務都聚焦到一個特定的任務上:讀數據庫、上傳文件、進行審計。換句話說,它們是活動。
當你創建workflow時,你會把這些單獨的活動捆在一起,並讓活動從一個轉到另一個。一些活動可作為其它活動的容器。一些活動執行一個單一任務,這我們已談過。基於容器的活動用來容納其它活動,在前一章中我們談及的root活動就是這種活動。root活動既可是一個順序活動也可是一個狀態機活動,本章中我們將對這些活動的類型進行說明。
活動怎樣知道在本步驟完成後下一步將做什麼呢?本章將主要把焦點放在這上面上。或許活動會以你創建一個root活動時指定的順序執行,或者可能是僅在一個特定的事件發生後才去執行一個指定的活動。為了讓我們更好地理解活動,我們首先要去看看WF Activity對象,然後看看活動是怎樣鏈接在一起的。
活動介紹:基本的工作單位
WF為你提供了一個Activity對象。Activity實現了一個看起來很簡單的基類。它不會做許多智能任務,但它可進行workflow的相互交互(這可不簡單)。活動對象由“Activity”派生,提供出了強悍的功能。你可自如地創建你自己的活動,這個話題將在第13章(自定義活動)進行介紹。事實上,本書的第二部分都是在介紹活動(第7章至第13章)。表4-1列出了許多我們通常感興趣的Activity的屬性,表4-2列出了你會經常用到的方法。在第13章,你還會看到更多的和自定義活動相關的方法和屬性。
表4-1 活動(Activity)的屬性
屬性 功能 Description 獲取或設置用戶定義的關於活動的描述。 Enable 獲取或設置一個指明實例能否被執行和驗證的值。 ExecutionResult 獲取實例最後運行的結果(ActivityExecutionResult)。(有Canceled、Compensated、Faulted、None和Succeeded)。 ExecutionStatus 得到workflow的狀態,其為ActivityExecutionStatus的枚舉值(Canceling、Closed、Compensating、Executing、Faulting和Initialized)之一。 Name 獲取或設置活動實例的名稱。 Parent 獲取包含本活動的父活動。 WorkflowInstanceId 獲取和該活動相關的workflow實例的標識符。
表4-2 活動(Activity)的方法
屬性 功能 Cancel 取消活動的執行。 Clone 返回活動的一個深拷貝。 Execute 以同步方式運行活動。 GetActivityByName 假如在一個組合活動上執行,本方法將返回組合活動中所包含的指定名稱的活動。 Load 從一個流中加載一個活動的實例。 RaiseEvent 觸發一個和指定的依賴屬性相關的事件。 RaiseGenericEvent<T> 觸發和所引用的依賴屬性相關的事件。RaiseEvent和RaiseGenericEvent的作用是一樣的——第一個事件RaiseEvent直接指出DependencyPropenty,而RaiseGenericEvent則是一個泛型版本。 Save 把活動保存到流中。
活動的方法通常都具有虛擬和受保護的屬性。目的是你可去覆蓋它們,使其提供一個符合你自己的活動所需要的實現。目前為止,最關鍵的方法是Execute。當workflow運行時調用這個方法時,你的活動便開始執行了。
活動可被分為兩個大類:組合活動和基本活動。組合活動包含其它活動。一個極好的例子是我們目前為止貫穿書中的Sequential活動(譯者注:它是基於順序工作流中所有活動的載體,在創建一個順序工作流時Visual Studio就已為我們創建好了,可在視圖設計器中看到)。目前為止所有的程序實例執行workflow實例的方式都是Sequential活動,它包含其它活動,如它自身、Delay活動和Code活動。
基本活動,就像我剛談到的Delay活動和Code活動,它們是一個基於單一任務的活動,我在本章早些時候談過它。最終,你需要基本活動去實際承載特定的任務。組合活動或許可指揮任務和數據的流動,但基本活動能做更多。
ActivityExecutionContext對象
許多Activity對象的方法需要一個ActivityExecutionContext對象來進行輸入。在workflow運行時把你要執行的workflow實例入隊的時候ActivityExecutionContext對象被創建,因此,它不是你直接要創建的對象。workflow運行時為你創建它。
ActivityExecutionContext對象的作用是提供活動以方法和服務,以便和workflow實例掛鉤。這些如初始化,定時器和產生執行流。它本質上是一個helper對象。在13章將更詳細的對活動上下文(環境)進行討論。
備注:假如你熟悉ASP.NET編程的話,這個context對象本質上和System.Web.HttpContext對象的作用是一樣的。其它相似的還有System.Threading.Thread.CurrentContext。所有這些Context對象的目標都是一樣的:提供一個存儲位置並容易地恢復一個當前執行實例的信息。這種情況下,它是一個執行當中的活動的一個實例。
依賴屬性(Dependency Properties)
在表4-2中,你將看到一些依賴屬性(DependencyProperty)。什麼是依賴屬性呢?
通常,假如你為類創建了一個屬性的話,你也會在類中創建一個字段來存儲該屬性的值。普遍的代碼如下:
class MyClass
{
protected Int32 _x=0;
public Int32 X
{
get { return _x;}
set { _x = value; }
}
}
字段_x更正式的叫法是backing store。在這個例子中,你的類為X屬性提供了backing store。
然而,WF和WPF通常都非常需要去訪問你類中的屬性。WPF需要指明容器中控件的空間和大小以便能最佳地被render。WF需要依賴屬性來方便地進行活動綁定。WF中ActivityBind類可為你方便地進行活動綁定。
活動驗證
活動通常都具有驗證能力,你可回憶第一章。
在第一章的例子中有這樣一種情況,如在IfElse活動中未指定應該選擇哪一個分支進行執行的條件時,Visual Studio會提醒我們。其它活動實現了不同的驗證算法。假如我們編譯帶有驗證錯誤的代碼,我們的編譯都會失敗。我們必須糾正這些驗證條件不充分的代碼,才能編譯和執行我們的workflow代碼。
workflow類型
你已創建過workflow應用程序,因此你可能注意到可以創建不同類型的workflow應用。
workflow應用程序的類型很大程度上依賴於你選擇的root活動。
盡管你注意到在新項目對話框中僅僅只有兩種workflow類型的應用程序可供選擇,
但實際運用中存在三種主要的類型。迄今為止本書中你已經創建過順序工作流,因此它們並不神秘。
當你創建workflow時,你的活動以你規定的順序執行。
另一種從新項目對話框中看到的workflow類型是狀態機工作流。我將在本章討論它的更多細節。
第三種workflow類型基於順序工作流,但它是規則驅動的。它不是僅僅執行你指定的任務,
而是由Policy活動和規則條件組成的基於規則的workflow,來執行基於你指定的業務規則workflow任務。
我們將在12章更多地學習這種workflow類型:“Policy活動”。因為這種類型的workflow以順序活動作為root,
因此在新項目對話框中沒有這種類型的workflow應用程序的模板可供選擇。你應以順序工作流作為起始,然後增加基於規則的活動。
選擇一種workflow類型
在什麼情況下一種類型的workflow比另一種類型的workflow更好?你如何選擇合適的workflow類型呢?
表4-3可為你提供一些基本的參考。
表4-3 選擇基本的workflow類型的判定表
workflow類型 適用條件 順序工作流 workflow任務可以自治的執行,很少由外部進行控制。主要由workflow自身來對執行的任務進行控制。只有少量用戶或沒有用戶來和它進行交互。它的root活動是SequentialWorkflow活動。 狀態機工作流 workflow任務嚴重依賴外部來控制和指示其執行。預期有很多的用戶交互(或其它外部控制)。對於基於狀態的workflow,root活動是StateMachineWorkflow活動。 基於規則的工作流 業務邏輯中包含復雜的判斷條件,既不像順序工作流也不像狀態機工作流。基於規則的工作流或者有一個順序的root活動,或者有一個基於狀態的root活動。 順序工作流的理想應用是去執行業務處理。假如你需要從源中讀數據,處理這些數據,發送通知,往你的一個數據池中寫入結果的話,順序工作流或許將符合你的需求。這並不意味著順序工作流不適合處理依賴於用戶交互的特定任務,如贊同或不同意之類的審批任務。其實這樣一些的用戶交互不應成為workflow自身的關注焦點。
假如你需要大量的用戶交互,當你的workflow發送通知給用戶或其它系統(有各種原因:通知、需要批復、選擇一個選項等等)以使用戶或其它系統進行響應(它們的響應來自事件)時,狀態機工作流可能是更好的選擇。這些事件觸發了workflow從一種處理狀態轉化到另一種處理狀態。我將在本章後面及14章(“基於狀態的工作流”)更多地討論這些。
最後一種workflow類型(我們將在12章看到)是基於規則的workflow。這些workflow基於業務規則判定是否進行轉化,並判定轉化後的目標是什麼。這些workflow通常都預置了更加復雜的劇情。
你或許會認為所有的workflow都能以基於規則的工作流類型來創建,但我們通常並不總是使用這種方式進行創建。因為其它的workflow類型,如順序工作流和狀態機工作流,它們能更容易地創建和測試。
要用最合適的workflow類型來構建你的系統。通常,在許多真實案例中你會發現你自己使用了所有三種workflow類型的組合。
順序活動
讓我們進一步深入順序復合活動吧。盡管迄今為止我們使用這些活動貫穿本書,但我在之前有意地拖延談論關於它的更多內容。現在我們去理解了workflow運行時和workflow實例是怎樣工作的,並且知道workflow實例是我們正運行中的workflow活動的版本,我們能更好的了解發生了什麼。
執行順序活動意味著這些活動以一個指定的順序執行。首先要做的事最先執行,最後才做的事最後執行。一個順序活動就像在根據目錄執行。你需要記下首先要做的任務,接下來要做的任務和最後要做的任務。假如這些任務以順序活動的方式存儲,WF將以你指定的順序精准地執行每個任務。
備注:本書中我們不會看到以動態的方式添加活動,但你應知道這是可以做到的。
在Visual Studio中,workflow的視圖設計器可幫你展示你的workflow。當你創建一個順序工作流應用程序並在設計器中打開root活動時,你可把任務放到屏幕的最上方以便首先被執行。那些朝向底部的任務將晚些執行。從可視化界面可看出,活動運行的順序是從上到下。當然順序活動還可以是一個復合活動。
創建順序工作流
在本書中迄今為止我們已創建過一些順序工作流應用程序,因此這裡我不再創建它們。但我還是把完整的步驟重復一下。
建立一個順序工作流應用程序
1.打開Microsoft Vistual Studio 2008。
2.在文件菜單上,選擇新建項目。然後將呈現新項目對話框。
3.在項目類型面板中,展開Vistual C#樹形節點,呈現出基於workflow項目的模板。
4.在模板面板中,點擊順序工作流控制台應用程序或順序工作流庫。前者創建一個可執行的應用程序並以控制台的方式執行,而後者創建一個動態鏈接庫並在其它應用程序中使用。
5.輸入你的項目或應用程序的名稱。
6.輸入或選擇你想保存你的項目的所在路徑。
7.點擊確定,Visual Studio 2008將為你建立一個基本項目,其中包含workflow視圖設計器用戶界面。
然後,你就可方便地從工具箱中拖拽你需要的活動,調整它們的屬性以符合你的需求。假如你需要增加更多workflow庫的項目,你可參考我前一章中的描述,或者簡單地直接在你的應用程序中增加一個新的workflow類。接下來我們還會看到大量的例子。
狀態活動
迄今為止在本書中我們還未看到過狀態機工作流。14章完全把焦點放到基於狀態的工作流的工作上,但我在這裡將介紹一些概念,我們也會快速地創建一個基於狀態的工作流。
看看這樣一個術語:有限狀態機。我們把這個術語分成三個詞:有限、狀態和機器。有限,意思是我們將進行轉化的狀態的數目是有限的。狀態是我們的應用程序在事件發生時進行轉化的邏輯條件。機器則意味自動化。我將用一個例子來闡明。
在工程學校,或許會要求你使用有限狀態機來設計一些數字系統。例如自動售貨機和洗衣機。看看自動售貨機,思考一下機器工作必須具有的步驟,以便它能為你提供你需要的商品(如汽水、糖果、點心等等)。當你投入硬幣時,它會合計你投入的硬幣金額,直到你投入的硬幣金額能購買商品時為止。當你選擇一個商品時,它會檢查存貨清單。假如有貨,它就把你選擇的東西分發給你。
我們可以使用有限狀態機來構建自動售貨機。在有限自動機的圖示中,我們使用圓來表示狀態,箭頭來表示狀態之間的轉換,轉換由事件觸發。圖中有一個邏輯上的起點和一個或多個邏輯上的終點。假如我們停在其它地方,我們的應用程序就被稱作未指定狀態或者無效狀態。我們的工作就是防止無效狀態,如我們不能免費地獲取商品,我們也不應該接收超過商品價格的多余的錢。假如自動售貨機接受了錢但又未提供商品的話,用戶毫無疑問會暴怒。
假象一下簡化的自動售貨機,讓我們畫出狀態和導致狀態轉換的事件吧。正如我提到的,狀態用圓來表示。使你的機器從一個狀態變為另一個狀態的事件用箭頭表示。它們都可命名以便我們知道這些是什麼狀態和相關的轉換。我們毫無疑問需要一個開始狀態,如下圖:
圖4-2 有限狀態機起始狀態符號
這個狀態表示機器所處的這樣一個位置:等待有人來投入一個硬幣。因此看看當有人來投入一個硬幣,但它還不夠買一個商品的情況,我們通過創建一個新狀態來進行模擬,這個狀態叫WaitCoins(等待硬幣)狀態,通過CoinInserted(投入硬幣)事件轉換到該狀態,如圖4-3:
圖4-3 轉換到WaitCoins狀態
在用戶投入足夠金額的錢以能購買其中的商品之前,機器一直處在WaitCoins狀態,並接受CoinInserted事件,否則會觸發SufficientCoins(金額足夠)事件使我們的機器轉到WaitSelection(等待選擇)狀態。在這裡我們的自動售貨機會耐心地等待用戶選擇一個商品。(在實際生活中,用戶也能在任何時候要回投入的硬幣,為了簡單起見,本例還是不考慮它吧。)
當用戶選擇商品後,商品會被分發給用戶,我們的(狀態)轉換也就結束了。完成狀態,或者稱作結束狀態,由二個圓圈來指明,參見圖4-4。
圖4-4
盡管這個自動售貨機在現實世界中或許太過於簡單,但此處只是期望為你提供一個的簡要描述,使你明白狀態機是如何工作的。當我們設計狀態機時,我們指明其離散的狀態,或者邏輯位置來等待事件發生,然後我們指明轉換機器狀態的事件。有些事件可讓機器返回到同一狀態,如開始狀態。其它事件則會在一個新的事件被處理後使機器轉換到一個新的狀態。沒有事件被觸發就沒有狀態的轉換,理想情況下應沒有無法預料的事件或異常。
這種模型和我們用過的順序工作流模型有很大的不同。在順序工作流裡,活動以指定的順序依次執行。一旦在該順序鏈上的一個活動執行完它的任務,在該鏈上的下一個活動就開始執行它的工作。在workflow處理中或許有事件參與其中,但它們在workflow任務的處理(如定時器事件)上相對簡單。
但狀態機會花費大量時間在等待上。它們等待事件,依賴事件來使它們的狀態進行轉換。狀態自身不會激發事件(盡管它們可能會調用外部代碼)。它們就是事件處理,因此它們會耐心地等待它們需要的事件來進行狀態的轉換。依靠事件,它完全可能從一個狀態切換到任何一個離散的不同的狀態。假如我們的自動售貨機處在WaitCoins狀態,當接受一個CoinInserted事件、RefundRequested事件或ImminentPowerdown事件時會分別做不同的事情。但我並未在圖4-4中這個經過簡化的模型裡畫出這些事件,但我相信你能看懂不同的事件是怎樣驅動你的有限狀態機轉換到不同狀態的。
在WF裡,基於狀態的workflow內的個別狀態由State活動創建。State活動是一個復合(組合)活動,但它對容納的子活動有限制。你將在14章學習到基於狀態的workflow的更多東西。
備注:正如順序工作流使用一個特別的Sequence活動來容納整個工作流一樣,基於狀態的工作流也有一個特別的root活動做這事,這就是StateMachineWorkflow活動,它是一個特別的State活動。特別之處是它是必須的,這樣當初始化執行時root活動就能接受初始化參數。
創建一個狀態機工作流應用程序
怎樣創建一個基於狀態的workflow呢?創建一個基於狀態的工作流和創建一個順序工作流一樣容易。我們現在就來看看怎樣創建一個狀態機工作流。然而我們現在不會添加任何代碼——雖然本書後面有很多時候需我們這樣去做,但我們現在只要需要了解怎樣創建一個狀態機工作流就行了。
創建一個狀態機工作流應用
1.啟動Microsoft Visual Studio 2008。
2.在文件菜單上,選擇新建一個項目,這將打開新建項目對話框。
3.展開項目類型面板中的Visual C#節點,這將顯示所有使用C#語言的項目類型。
4.在Visual C#節點下點擊Workflow節點,這將顯示所有基於工作流的項目模板。
5.在模板面板內,點擊狀態機工作流控制台應用程序或狀態機工作流庫。如下圖所示:
6.輸入程序或項目的名稱。
7.輸入或選擇項目文件要保存的位置。
8.點擊確定。Visual Studio 2008將為你創建一個包含workflow視圖設計器用戶界面的項目,如下圖:
我們需要去觸發引發工作流改變狀態的事件,因此,我們需理解workflow實例是怎樣和它們的宿主應用程序進行通信的。我們將在第八章(“調用外部方法”)看到宿主和workflow之間的通信,在第十章(“事件活動”)中我們將學習狀態機工作流的事件驅動。