你还记得windows workflow foundation吗
很多年前,windows workflow foundation還叫WWF,而直譯過來的名稱讓很多人以為它就是用來開發(fā)工作流或者干脆就是審批流的。
博主當(dāng)年還是個(gè)懵懂的少年,卻也知道微軟不會(huì)大力推一個(gè)面向如此具象的業(yè)務(wù)場(chǎng)景的技術(shù),于是特地找了一本《WF本質(zhì)論》,當(dāng)看到“程序即數(shù)據(jù)”這個(gè)論斷時(shí),被深深震撼了。可能這只是作者的隨意一寫,但當(dāng)時(shí)正是泛型方法、lamda表達(dá)式、匿名委托啥的開始出現(xiàn)的時(shí)候,作者的這一說法在某種程度上暗合了博主平常的編程思想。于是邏輯與數(shù)據(jù),算法與結(jié)構(gòu),它們之間的界限在我眼里,在我心里,開始以更詭異的方式模糊了起來。
然而之后并未在工作上使用過WF,因此博主也就不再關(guān)注此項(xiàng)技術(shù)。如今重新翻看,突然發(fā)現(xiàn)官方的Workflow Team blog最近的更新都是兩年前了,網(wǎng)上的資料都是N年前的,而且大多數(shù)是教大家怎么用。為何要用,何處要用,基本上沒有太多介紹。到了現(xiàn)如今,似乎也被微軟拋棄了,為此我還專門在博問里提了下,也沒人知道具體詳情。
很少有看到專門使用wf進(jìn)行開發(fā)的場(chǎng)景,好用與否,在小眾的群體里抽樣得出結(jié)論也就顯得不怎么可靠了。似乎wf在sharepoint環(huán)境中用起來比較協(xié)調(diào),由于博主對(duì)sharepoint沒有研究過,所以對(duì)此不好評(píng)論。如若在純代碼環(huán)境下開發(fā),用wf自帶的一套activity能畫出看似清晰的流程圖,然而關(guān)鍵的和外部交互的環(huán)節(jié)卻仍然避免不了,需要考慮的因素大部分仍然需要代碼去完成;而各種條件下流程的走向,使用activity的條件判斷,又讓人覺得沒有直接寫代碼來的方便,比如ifelse,用代碼可能只要在一個(gè)方法體中就能完成,而在流程圖中卻要使用兩個(gè)activity,如果其中再有交互跳轉(zhuǎn)的話,流程圖的復(fù)雜程度未必在代碼之下,而后期維護(hù)也難說容易。
不妨非惡意的揣測(cè),經(jīng)過幾年時(shí)間,微軟慢慢覺得,將代碼間原本隱晦的邏輯關(guān)系抽離出來變成看得見的“流程”,對(duì)程序員來說,未必有多大的意義,于是就減少了這方面的投入。當(dāng)然你也可以認(rèn)為wf已經(jīng)很成熟了,不過即使如此,博主還是傾向認(rèn)為此種所謂成熟是因?yàn)檎也坏礁倪M(jìn)的方向,雖然“成熟”,但不怎么好用。
but,wf并非一無是處,wf的bookmark(書簽機(jī)制)是最區(qū)別于傳統(tǒng)開發(fā)的特性,個(gè)人認(rèn)為也是最重要的特性。傳統(tǒng)開發(fā)時(shí),我們時(shí)不常的會(huì)遇到這種情形:判斷流程是否走到特定環(huán)節(jié)以便決定下一步操作,其實(shí)就是判斷A\B\C\D\...等排列組合變數(shù),它們之間可能也是相互關(guān)聯(lián)的,只要流程沒有繼續(xù)流轉(zhuǎn),那么每次啟動(dòng)都要進(jìn)行同樣的判斷;是否可以在各變數(shù)滿足條件時(shí)生成一個(gè)標(biāo)簽,當(dāng)我們發(fā)現(xiàn)這個(gè)標(biāo)簽存在時(shí),就可以去找標(biāo)簽對(duì)應(yīng)的環(huán)節(jié),就能馬上進(jìn)行后續(xù)操作了。實(shí)際開發(fā)中,我們常設(shè)置一個(gè)冗余的標(biāo)識(shí)字段起這個(gè)作用。但單個(gè)字段未必能完全標(biāo)識(shí)一個(gè)環(huán)節(jié),而且也顯得不夠直觀。wf中的bookmark與上述的標(biāo)簽類似,本質(zhì)上,它是對(duì)委托異步回調(diào)爐火純青的應(yīng)用,更自然的描述是在變數(shù)尚未符合條件時(shí)的一種“等待”,常用于與外部交互,在流程設(shè)計(jì)時(shí),也不會(huì)像傳統(tǒng)代碼操作(查詢、判斷、更新)標(biāo)簽?zāi)菢语@得突兀(這些操作wf都在更底層幫我們處理了)。
開頭說到微軟推出wf的本意并非用于開發(fā)業(yè)務(wù)上的工作流,我們甚至可以將任意有邏輯順序的一段代碼“wf化”,然而從業(yè)務(wù)場(chǎng)景來講,wf的很多概念能映射其中,畢竟抽象和具象,都逃不離流程二字。因此項(xiàng)目前期或工期緊張,需要快速開發(fā)的時(shí)候,wf也能幫助程序員梳理流程——在[對(duì)]業(yè)務(wù)流程尚不清晰的時(shí)候,你會(huì)發(fā)現(xiàn)這非常有用——就算日后擯棄wf,大部分代碼都可以復(fù)用,而由于各環(huán)節(jié)明確的流轉(zhuǎn)關(guān)系,代碼重構(gòu)也較為容易。博主就是因?yàn)檫@個(gè)原因,在目前的一個(gè)項(xiàng)目中使用了wf,下面我會(huì)簡單介紹相關(guān)要點(diǎn)和個(gè)人理解。
版本控制也是它的一個(gè)亮點(diǎn),這個(gè)就不細(xì)說了。
由于本人對(duì)wf鉆研不深,所述難免有誤,請(qǐng)各位同學(xué)批評(píng)指正。另,本文并不展示任何項(xiàng)目代碼,只以行業(yè)一般流程,展示博主在開發(fā)過程中的思索。
本項(xiàng)目主干是一手房置購流程,涉及到認(rèn)籌、認(rèn)購、成交、租賃四個(gè)環(huán)節(jié),需求簡化后如下(圖是用visio畫的,畫得很挫,一直沒找到實(shí)線弧形箭頭):
1、環(huán)節(jié)流轉(zhuǎn)
2、流轉(zhuǎn)時(shí)需審核(可跨級(jí)審核,若申請(qǐng)人本身是最后環(huán)節(jié)審核人則直接通過)
3、審核不通過時(shí)將退回發(fā)起環(huán)節(jié);審核過程中,申請(qǐng)人也可撤消此次申請(qǐng)
4、單個(gè)環(huán)節(jié)也有不同狀態(tài),比如認(rèn)購有已認(rèn)購和退認(rèn)購兩種狀態(tài),此類狀態(tài)轉(zhuǎn)換也需要審核;而內(nèi)容變更也需要審核
5、同時(shí)至多只能存在一個(gè)審核中事項(xiàng)。比如認(rèn)購內(nèi)容變更審核中時(shí),就不能發(fā)起轉(zhuǎn)成交申請(qǐng)。
這是一個(gè)比較典型的工作流場(chǎng)景。剛接手這個(gè)項(xiàng)目時(shí),我考慮用傳統(tǒng)方式開發(fā),不多久我發(fā)現(xiàn)自己在各種環(huán)節(jié)、狀態(tài)、標(biāo)識(shí)、互斥項(xiàng)間暈頭轉(zhuǎn)向,一團(tuán)漿糊。即使流程圖已經(jīng)很清晰明白地?cái)[在面前,將之賦予代碼和數(shù)據(jù),并使看似毫無關(guān)聯(lián)的各塊內(nèi)容按照預(yù)期邏輯運(yùn)行,似乎顯得相當(dāng)困難。特別是產(chǎn)品有時(shí)會(huì)一臉無奈地跟你說,認(rèn)籌也可以轉(zhuǎn)租賃,業(yè)務(wù)說的;隔天又說,不需要了。要維系代碼之間“隱秘”的邏輯關(guān)系并快速應(yīng)對(duì)需求變更,隨著業(yè)務(wù)復(fù)雜度的提高,難度也更快的升高。
當(dāng)然,對(duì)于熟練工來說,這點(diǎn)難度不算什么,畢竟我們每天都在做這種工作——將業(yè)務(wù)流程轉(zhuǎn)化成層層調(diào)用的代碼——我們還可以祭出設(shè)計(jì)模式、AOP、IOC、ORM啥的讓代碼看起來更清(fu)晰(za)。我們一直在接受面向?qū)ο蟆⒚嫦蚣軜?gòu)、面向服務(wù)的訓(xùn)練,而缺少真正面向流程編程的經(jīng)驗(yàn),我想這才是為什么微軟當(dāng)年會(huì)推廣wf的原因(此為病句,意為強(qiáng)調(diào))。
wf有三種內(nèi)置流程:順序流、Flowchart、StateMachine。本項(xiàng)目可考慮后兩種,博主選擇的是StateMachine,整出來的主流程如下:
看上去很凌亂,只能怪vs自帶的wf設(shè)計(jì)器沒有visio好用,其實(shí)上圖就是第1幅流程圖的wf版本。
再來看下每條連線的邏輯,以認(rèn)購環(huán)節(jié)出去的連線為例,觸發(fā)器如下:
可以看到,流程會(huì)依據(jù)外部信號(hào)決定下一步驟,流到下一個(gè)環(huán)節(jié)(轉(zhuǎn)成交、轉(zhuǎn)租賃),或者是退認(rèn)購,這里還有變更的情況圖中沒放出來。最終會(huì)根據(jù)流程中各變量和參數(shù)值選擇某個(gè)路線:
任何類型的審核不通過or申請(qǐng)人撤銷就回到當(dāng)前環(huán)節(jié),和之前我們用visio畫的流程圖一樣直觀。不過,這里有個(gè)問題。博主剛接觸statemachine時(shí),選擇entry還是trigger放置業(yè)務(wù)邏輯比較困惑,當(dāng)時(shí)認(rèn)為兩者皆可,畢竟最終只是通過condition來指定狀態(tài)的轉(zhuǎn)變方向,用不著care condition在哪里生成;博主當(dāng)時(shí)認(rèn)為微軟設(shè)計(jì)statemachine時(shí),是將trigger當(dāng)做bookmark activity的容器,以便于與外部決策進(jìn)行交互,當(dāng)然我們也可以將bookmark放在entry中,說到底這就是一個(gè)規(guī)范問題,不必深究。然而博主發(fā)現(xiàn),若流程從一個(gè)state流向同一個(gè)state——即state指向自己——其實(shí)前后兩個(gè)環(huán)節(jié)已不是同一個(gè)“實(shí)例”了 ,因此原state的變量等狀態(tài)不會(huì)保存,entry會(huì)再次執(zhí)行。為了避免這種狀況,有停留在當(dāng)前狀態(tài)的情形,應(yīng)該將業(yè)務(wù)邏輯寫在entry中,不進(jìn)入到觸發(fā)后的流轉(zhuǎn),因?yàn)橐坏┝鬓D(zhuǎn),就算流轉(zhuǎn)回當(dāng)前狀態(tài),也是先出再進(jìn)。當(dāng)然,如果當(dāng)前狀態(tài)沒有需要保留的信息,寫在trigger亦可。
再來看看審核到底發(fā)生了什么。
這個(gè)更不用我多說了,不過我還是要多說兩句。雖然上圖很清晰地還原了對(duì)應(yīng)的需求,但未必是最合適的做法,我最后還是將這些邏輯統(tǒng)一到一個(gè)activity里。前面說過,我們能將絕大多數(shù)多行代碼wf化,but,復(fù)雜的流程需要借助wf,一個(gè)簡單方法就能搞定的邏輯如果還硬要畫出幾道條條理理來,那就是偏執(zhí)了。(話說,寫程序的大多數(shù)人都是偏執(zhí)的,非黑即白,我沒說錯(cuò)吧)
對(duì)于前述需求的第5條,單條wf流程天生就能滿足,不需要我們做額外的coding。思考一下,若幾個(gè)審核可以并行,或流程流轉(zhuǎn)到下一環(huán)節(jié)后,上一環(huán)節(jié)需要變更了,怎么辦?用多流程或子流程試試吧。
代碼活動(dòng)中普通的成員變量,持久化在bookmark恢復(fù)后,值丟失不可用;若需要在持久化前后保持變量值,應(yīng)該使用Variable,或應(yīng)用Serializable特性,兩者都是博主推測(cè)沒試過。
有些事務(wù)不能失敗了就全部回退重來,比如單據(jù)狀態(tài)從a->b,經(jīng)過多位領(lǐng)導(dǎo)審批同意后,結(jié)果因?yàn)樽詈笠徊教峤徊怀晒?#xff0c;讓單據(jù)回退到a重審?這種情況,只能將最后一步重新提交,實(shí)際開發(fā)中,可將此類“臟提交”定時(shí)再提交。
一個(gè)activity里可create多個(gè)bookmark,當(dāng)所有bookmark都恢復(fù)執(zhí)行后,流程才繼續(xù)往下。
wf在流程流轉(zhuǎn)過程中,并不能返回?cái)?shù)據(jù)給調(diào)用者,比如發(fā)起認(rèn)籌轉(zhuǎn)認(rèn)購的申請(qǐng),調(diào)用ResumeBookmark方法,并不能知曉是直接通過還是等待審核,需要另外查數(shù)據(jù)庫得到狀態(tài)結(jié)果。當(dāng)然可以使用WorkflowApplication.Extensions與外部程序交互,但不能滿足某些場(chǎng)景,比如客戶端調(diào)用webapi,服務(wù)端action發(fā)起流程,action自然需要知道流程跑完后(暫停or完結(jié))的結(jié)果是什么并返回給客戶端;這時(shí)候WorkflowApplication.Extensions就然并卵了,除非改寫底層,比如讓wf能取消action的后續(xù)執(zhí)行并將結(jié)果數(shù)據(jù)寫入http連接。
流程改版or外部依賴項(xiàng)變化,wf下,要么新舊版本并行,要么研究如何將舊版本遷移到新版本,很多情況下沒有純代碼控制來得方便。
博主觀點(diǎn):同步SaaS并非wf較適用的場(chǎng)景,wf適用于異步消息推送場(chǎng)景,比如外賣點(diǎn)餐狀態(tài)、快遞狀態(tài)、銀行資金流轉(zhuǎn)等,客戶端并不等待馬上的結(jié)果,而是事項(xiàng)狀態(tài)改變時(shí)接收服務(wù)端消息即可。當(dāng)然硬要在同步環(huán)境下使用也可以,此時(shí)需要更松散的設(shè)計(jì)和底層框架的配合,以適應(yīng)wf“封閉式流程”的特點(diǎn)。
需繼續(xù)研究的點(diǎn):
1、有些流程大同小異,能否封裝成一個(gè)可配置的流程,在設(shè)計(jì)時(shí)進(jìn)行簡單的參數(shù)配置就能顯示不同的流程步驟;比如a狀態(tài)可轉(zhuǎn)為b、c狀態(tài),而b只能轉(zhuǎn)c狀態(tài),那么a,b在轉(zhuǎn)換到下一個(gè)環(huán)節(jié)的判斷邏輯就有少許差異了。這貌似只能通過元數(shù)據(jù)實(shí)現(xiàn)。
2、InstanceOwner,網(wǎng)上實(shí)在找不到更多關(guān)于它的介紹,目前可知是用于多宿主環(huán)境下,宿主對(duì)wf實(shí)例的lock。沒找到給實(shí)例賦值InstanceOwner的直接途徑,網(wǎng)上找到的基本上是下面兩行代碼:
InstanceView view = instore.Execute(instore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30)); instore.DefaultInstanceOwner = view.InstanceOwner;不知是基于什么因素考慮,總是覺得不太理解是什么意思,為什么要以及以這種方式設(shè)置DefaultInstanceOwner,如果不設(shè)置的話,那么下面這行代碼運(yùn)行時(shí)就會(huì)報(bào)錯(cuò):
WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(Guid.Parse(flowinstance.InstanceID), instore);框架應(yīng)該完全可以在底層就自動(dòng)給我們處理InstanceOwner相關(guān)的東東,比如下面這句在沒設(shè)置DefaultInstanceOwner的時(shí)候就不會(huì)報(bào)錯(cuò):
wfApp.Load(Guid.Parse(flowinstance.InstanceID));如何能設(shè)置自己的InstanceOwner呢,maybe可以通過InstanceOwnerMetadata。
?
CreateWorkflowOwnerCommand createCommand = new CreateWorkflowOwnerCommand() {InstanceOwnerMetadata ={{ WorkflowHostTypeName, new InstanceValue(WFInstanceScopeName) },} };?
不過設(shè)置完了,怎么獲取又是一個(gè)問題,有興趣的朋友可以研究下這個(gè)文件,也可以在官方WF_WCF_示例里找到。總之持久化這趟子水夠深。
?
其它參考資料:
Loading persisted workflow instances with WorkflowApplication
總結(jié)
以上是生活随笔為你收集整理的你还记得windows workflow foundation吗的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 6000mAh超大电池 华为畅享50今日
- 下一篇: 【转】SharePoint 2013中修