WCF揭秘学习笔记(5):WF定制活动
WF(Windows Workflow Foundation,Windows工作流基礎(chǔ))為.NET提供了一種基于模型的、聲明方式的過(guò)程執(zhí)行引擎,它改變了傳統(tǒng)的通過(guò)一行行編寫(xiě)代碼來(lái)開(kāi)發(fā)服務(wù)功能的方式。
WF包含三個(gè)核心組件:活動(dòng)框架(activity framework)、運(yùn)行時(shí)環(huán)境(runtime environment)、工作流設(shè)計(jì)器(workflow designer)。
WF不是什么
工作流這個(gè)詞在軟件開(kāi)發(fā)領(lǐng)域和相關(guān)社區(qū)里已經(jīng)被“濫用”了。所以弄清楚WF在這些流行的工作流概念中到底指的是哪一種就非常重要。
1. WF不是服務(wù)器,雖然可以將工作流功能集中起來(lái)然后通過(guò)服務(wù)器暴露給其他應(yīng)用程序使用。
2. WF不是一種BPM(Business Process Management,業(yè)務(wù)流程管理)工具,雖然可以將WF作為工作流執(zhí)行引擎來(lái)創(chuàng)建BPM工具。
3. WCF并不直接面向業(yè)務(wù)分析人員,雖然可以使用可重新承載的設(shè)計(jì)器向業(yè)務(wù)分析人員提供創(chuàng)建自己的工作流的工功能。WF非常靈活,它允許將此功能集成到分析人員熟悉的環(huán)境中。如果現(xiàn)有的設(shè)計(jì)器不能滿足要求,還可以創(chuàng)建定制的工作流設(shè)計(jì)器。
4. WF不是企業(yè)應(yīng)用集成工具,雖然可以將第三方的系統(tǒng)功能封裝在活動(dòng)中,然后將它們加到工作流里。
5. WF不是一個(gè)玩具。這可以在具有大量服務(wù)器集群的企業(yè)級(jí)規(guī)模環(huán)境里高性能地運(yùn)作。它可以勝任企業(yè)級(jí)應(yīng)用,SharePoint Server就是一個(gè)很好的應(yīng)用實(shí)例。而WF本身并不是一個(gè)企業(yè)級(jí)應(yīng)用,它只是一組開(kāi)發(fā)套件。
WF不僅可以用在基于服務(wù)器的應(yīng)用程序里,它還可以用在WinForm應(yīng)用中執(zhí)行從服務(wù)協(xié)調(diào)到用戶界面定制的任何應(yīng)用程序邏輯。也可以用在Web應(yīng)用程序中以管理流程狀態(tài)。總之,任何可以編寫(xiě).NET代碼的地方都可使用它作為提供邏輯的代碼。
活動(dòng)
活動(dòng)(activity)是WF的基本構(gòu)建模塊。從復(fù)雜的執(zhí)行邏輯到執(zhí)行一個(gè)對(duì)SQL數(shù)據(jù)庫(kù)的更新,這些操作都被封裝到一個(gè)個(gè)獨(dú)立的工作單元,也就是活動(dòng)中。活動(dòng)是所有直接或間接繼承自System.Workflow.ComponentModel.Activity的類(lèi)。活動(dòng)具有兩個(gè)方面的行為:運(yùn)行時(shí)行為(runtime behavior)和設(shè)計(jì)時(shí)行為(design-time behavior)。
運(yùn)行時(shí)行為是活動(dòng)在工作流中使用時(shí)執(zhí)行的代碼。它可能是訪問(wèn)一個(gè)Web服務(wù)或執(zhí)行一塊代碼,也可能是協(xié)調(diào)子活動(dòng)的執(zhí)行。一個(gè)常被問(wèn)及的問(wèn)題是:WF的性能怎樣?在活動(dòng)級(jí)別,答案很簡(jiǎn)單:活動(dòng)可以和在.NET程序集里相同代碼的執(zhí)行速度一樣快。因?yàn)榛顒?dòng)只是一個(gè)已經(jīng)編譯好的.NET程序集,它包含從activity繼承的類(lèi)。管理活動(dòng)的生命周期需要額外的開(kāi)支。
當(dāng)包含活動(dòng)的工作流被創(chuàng)建時(shí),該活動(dòng)就會(huì)被初始化并停留在Initialized(初始)狀態(tài)。也就是說(shuō),當(dāng)CreateInstance方法被調(diào)用且數(shù)據(jù)流實(shí)例被創(chuàng)建時(shí),所有的活動(dòng)都將被初始化。服務(wù)在被調(diào)度執(zhí)行時(shí),其狀態(tài)會(huì)變成Executing(執(zhí)行)。如果一切正常,活動(dòng)會(huì)進(jìn)入到Closed(關(guān)閉)狀態(tài),也就是它的任務(wù)已完成,可以休息了。
活動(dòng)可能會(huì)遇到錯(cuò)誤,從而進(jìn)入Faulting狀態(tài)而被關(guān)閉。活動(dòng)也可能被其他的活動(dòng)取消掉,從而進(jìn)入Canceling狀態(tài),然后再進(jìn)入Closed狀態(tài)。最后,如果活動(dòng)為其執(zhí)行定義了回滾或補(bǔ)償操作,活動(dòng)就會(huì)從Closed狀態(tài)激活并進(jìn)入到Compensating狀態(tài)。由于某些原因,一個(gè)錯(cuò)誤或一個(gè)取消處理都需要調(diào)用補(bǔ)償邏輯。
另外,活動(dòng)的設(shè)計(jì)體驗(yàn)對(duì)創(chuàng)建工作流是非常重要的。活動(dòng)可能需要在設(shè)計(jì)窗口里有些個(gè)性化的表示,目的是為了告訴開(kāi)發(fā)者可以一目了然地知道活動(dòng)正在做什么。
開(kāi)箱即用活動(dòng)
隨WF一起發(fā)布的活動(dòng)通常被稱(chēng)為開(kāi)箱即用活動(dòng)(out of the box activity)。它們是一組基本的活動(dòng),可用來(lái)創(chuàng)建簡(jiǎn)單的工作流和組建新的活動(dòng)。下表給出了每個(gè)開(kāi)箱即用活動(dòng)的簡(jiǎn)單介紹:
創(chuàng)建定制服務(wù)
工作流開(kāi)發(fā)者可能經(jīng)常需要?jiǎng)?chuàng)建定制活動(dòng)。從封裝經(jīng)常使用的方法到創(chuàng)建能為一種新的執(zhí)行模式建模的復(fù)合活動(dòng),開(kāi)發(fā)者在開(kāi)始一個(gè)工作流項(xiàng)目時(shí)需要首先思考他需要什么樣的活動(dòng)。隨著時(shí)間的推移,這些活動(dòng)可被復(fù)用或被用來(lái)組建更高層次的活動(dòng),最后就像處理現(xiàn)在的普通對(duì)象一樣。
基礎(chǔ)
創(chuàng)建定制活動(dòng)的最基本方法就是從System.Workflow.ComponentModel.Activity繼承一個(gè)類(lèi)。這將為活動(dòng)創(chuàng)建所有的基本機(jī)制,除了活動(dòng)實(shí)現(xiàn)的實(shí)際邏輯。可通過(guò)重載Execute方法來(lái)實(shí)現(xiàn)實(shí)際的邏輯。如下所示:
public class BasicActivity : Activity{
public BasicActivity()
{
this.Name = "BasicActivity";
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
Console.WriteLine("Basic Activity");
return ActivityExecutionStatus.Closed;
}
}
Execute方法執(zhí)行活動(dòng)的操作并需同時(shí)通知運(yùn)行時(shí)它的狀態(tài)。在這里,它返回Closed狀態(tài),表示該活動(dòng)已完成它的工作。一個(gè)更加復(fù)雜的模式是:返回Executing狀態(tài),同時(shí)等待一個(gè)需要運(yùn)行較長(zhǎng)時(shí)間的工作完成。當(dāng)這個(gè)較長(zhǎng)時(shí)間的工作完成時(shí),活動(dòng)將通知運(yùn)行時(shí)它已經(jīng)完成了。在等待時(shí),工作流實(shí)例可能停頓下來(lái)較長(zhǎng)時(shí)間。
我們經(jīng)常需要控制一些與活動(dòng)相關(guān)的參數(shù),它們會(huì)影響活動(dòng)所提供的有用功能。最簡(jiǎn)單的方法就是為活動(dòng)類(lèi)添加屬性,如下所示:
public string TextToPrint{
get { return textToPrint; }
set { textToPring = "value"; }
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
Console.WriteLine("Text to print: {0}", TextToPrint);
return ActivityExecutionStatus.Closed;
}
當(dāng)使用XAML聲明式地創(chuàng)建工作流時(shí),這些屬性會(huì)被作為活動(dòng)的特性(attribute),如下所示:
<SequentialWorkflowActivity x:Class="SampleWFApplication.Workflow1"x:Name="Workflow1"
xmlns:ns0="clr-namespace:SampleWFApplication"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
<ns0:BasicActivity TextToPrint="Hello World"
x:Name="basicActivity1" />
</SequentialWorkflowActivity>
這樣就允許在設(shè)計(jì)器里設(shè)定屬性的值從而定制活動(dòng)的行為。但這還是靜態(tài)的,屬性被限制為設(shè)計(jì)時(shí)設(shè)定的值。當(dāng)創(chuàng)建的工作流需要評(píng)估傳入的定制對(duì)象時(shí)呢?這可以通過(guò)使用依賴屬性(dependency property)來(lái)實(shí)現(xiàn)。依賴屬性和之前介紹的.NET標(biāo)準(zhǔn)屬性類(lèi)似,但在聲明和用戶法上有所不同。VS中有內(nèi)建的代碼來(lái)創(chuàng)建它們,如下所示:
public static DependencyProperty OrderAmountProperty = System.Workflow.ComponentModel.DependencyProperty.Register("OrderAmount", typeof(int), typeof(BasicActivity));
[Description("This property holds the amount of the order")]
[Category("Order Details")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int OrderAmount
{
get
{
return ((int)(base.GetValue(BasicActivity.OrderAmountProperty)));
}
set
{
base.SetValue(BasicActivity.OrderAmountProperty, value);
}
}
這個(gè)稍長(zhǎng)的聲明里不僅有屬性的聲明,而且還包含了一個(gè)靜態(tài)DependencyProperty變量的聲明。DependencyProperty是一種添加到DependencyObject(Activity就繼承自DependencyObject)的特殊屬性類(lèi)型。DependencyProperty不同于傳統(tǒng)屬性,它可以支持三種特殊的使用方式:
1. 活動(dòng)綁定。
2. 元數(shù)據(jù),只在設(shè)計(jì)時(shí)賦值,在運(yùn)行時(shí)不能更改。
3. 附加屬性(attached property),動(dòng)態(tài)地為活動(dòng)添加屬性。
使用依賴屬性的最普通場(chǎng)景是支持活動(dòng)綁定。使用依賴屬性的好處是可以添加新的設(shè)計(jì)時(shí)行為。將活動(dòng)拖到設(shè)計(jì)窗口后,觀察它的屬性窗口,會(huì)發(fā)現(xiàn)在剛長(zhǎng)聲明的屬性旁邊新增一個(gè)new圖標(biāo)。單擊該圖標(biāo)會(huì)彈出一個(gè)新的Bind(綁定)對(duì)話框。
Bind對(duì)話框允許將屬性的值與工作流中的另外一個(gè)值動(dòng)態(tài)綁定。這個(gè)值可以是在工作流創(chuàng)建時(shí)傳入該工作流的一個(gè)屬性,也可以是另外一個(gè)活動(dòng)的屬性。在設(shè)計(jì)時(shí),活動(dòng)被告知到哪兒去查找依賴屬性的值。通過(guò)選擇工作流中同一類(lèi)型(可以是一個(gè)定制類(lèi)型)的另外一個(gè)值,一個(gè)綁定表達(dá)式會(huì)出現(xiàn)在屬性窗口里作為這個(gè)依賴屬性的值。表達(dá)式與下面的式子類(lèi)似的:
Activity=Workflow1, Path=WorkflowOrderAmount
式子的第一部分表示源(這里是父工作流),然后是活動(dòng)可以使用的屬性。式子里可以使用點(diǎn)操作符,如果希望被綁定的值是屬性的下面幾層,它也可以被訪問(wèn)。依賴屬性的值還可以直接寫(xiě)在代碼里。
Bind對(duì)話框里有一個(gè)Bind to a New Member(綁定到一個(gè)新成員)標(biāo)簽頁(yè)。這個(gè)窗口可以使一個(gè)活動(dòng)的依賴屬性“提升”(promote)為窗口活動(dòng)(即包含活動(dòng)的活動(dòng))的一個(gè)成員。
組合
第二種創(chuàng)建活動(dòng)的方法是復(fù)合(composition),這也是VS的默認(rèn)方法。以下代碼在VS中新建一個(gè)活動(dòng)類(lèi)的定義:
public partial class ACompositeActivity : SequenceActivity它繼承自SequenceActivity。SequenceAcitivity是創(chuàng)建順序執(zhí)行工作流的基類(lèi),它的Execute方法負(fù)責(zé)將包含的活動(dòng)依次調(diào)度執(zhí)行。通過(guò)繼承,可保留我們希望的這種模式。這將使活動(dòng)的開(kāi)發(fā)者通過(guò)拖拽其他活動(dòng)到新的活動(dòng)里來(lái)創(chuàng)建一個(gè)新的活動(dòng),也就是從現(xiàn)有的活動(dòng)創(chuàng)建新的活動(dòng)。這是一個(gè)創(chuàng)建新工作單元的強(qiáng)大工具。這意味著,如果有一組已經(jīng)實(shí)現(xiàn)且足夠強(qiáng)大的基本活動(dòng)(如對(duì)現(xiàn)有API的封裝)的集合,就可使用它們快速創(chuàng)建更高層次的新功能單元。
考慮下面的活動(dòng):
1. SendEmail
2. LookupManager
3. WaitForResponseToEmail
使用這些活動(dòng)及一些開(kāi)箱即用活動(dòng)提供的結(jié)構(gòu)化活動(dòng),可以創(chuàng)建任意復(fù)雜的標(biāo)準(zhǔn)活動(dòng),然后新建的活動(dòng)就可以作為批準(zhǔn)活動(dòng)被任意的工作流使用。
定制復(fù)合活動(dòng)
我們還可以創(chuàng)建另外一類(lèi)活動(dòng),即定制復(fù)合活動(dòng)(custom composite activity)。復(fù)合活動(dòng)包含子活動(dòng),它的Execute方法負(fù)責(zé)調(diào)度執(zhí)行這些子活動(dòng)。在開(kāi)箱即用活動(dòng)中,Sequence、While和Parallel都是復(fù)合活動(dòng)的例子。
復(fù)合活動(dòng)執(zhí)行時(shí),會(huì)執(zhí)行它的子活動(dòng)并訂閱這些子活動(dòng)的完成事件。活動(dòng)會(huì)返回Executing狀態(tài),向運(yùn)行時(shí)指明它要繼續(xù)執(zhí)行下一個(gè)被調(diào)度的活動(dòng)。通過(guò)接收子活動(dòng)的完成事件,復(fù)合活動(dòng)可以繼續(xù)調(diào)度執(zhí)行其他的活動(dòng)或判斷是否可以結(jié)束。當(dāng)所有的活動(dòng)都結(jié)束了或復(fù)合活動(dòng)決定結(jié)束時(shí),它會(huì)返回Closed狀態(tài),表明它已經(jīng)結(jié)束了。工作流會(huì)確保在允許一個(gè)活動(dòng)關(guān)閉前,它的所有子活動(dòng)都關(guān)閉了。
活動(dòng)通信
工作流并不是在完全孤立的環(huán)境里執(zhí)行的,它們經(jīng)常需要和宿主程序交互以發(fā)送消息給宿主或從宿主接收消息。兩個(gè)開(kāi)箱即用活動(dòng)被設(shè)計(jì)成支持這樣的交互:Handle-ExtenalEvent和CallExternalMethod。這些活動(dòng)通過(guò)宿主和工作流間共享的契約通信。該契約的實(shí)現(xiàn)作為本地服務(wù)提供給運(yùn)行時(shí)ExternalDataExchangeService。
讓我們來(lái)看一個(gè)基于員工調(diào)查應(yīng)用場(chǎng)景的例子。首先創(chuàng)建一個(gè)在宿主和工作流間共享的契約,并為其添加ExtenalDataChange特性:
using System;using System.Workflow.ComponentModel;
using System.Workflow.Activities;
namespace ExternalEventSample
{
[ExternalDataExchange()]
public interface ISurveyResponseService
{
void SurveyEmployee(string employee, string surveyQuestion);
event EventHandler<SurveyEventArgs> SurveyCompleted;
}
[Serializable]
public class SurveyEventArgs : ExternalDataEventArgs
{
private string employee;
public string Employee
{
get { return employee; }
set { employee = value; }
}
private string surveyResponse;
public string SurveyResponse
{
get { return surveyResponse; }
set { surveyResponse = value; }
}
public SurveyEventArgs(Guid instanceId, string employee, string surveyResponse) : base(instanceId)
{
this.employee = employee;
this.surveyResponse = surveyResponse;
}
}
}
該接口定義了一個(gè)事件、一個(gè)定制的事件參數(shù)類(lèi)和一個(gè)公共方法。我們?cè)偬峁┮粋€(gè)接口的實(shí)現(xiàn):
using System;namespace ExternalEventSample
{
class SurveyResponseService : ISurveyResponseService
{
public void SurveyEmployee(string employee, string surveyQuestion)
{
//here we would notify and display the survey
Console.WriteLine("Hey {0}, what do you think of {1}?", employee, surveyQuestion);
}
public event EventHandler<SurveyEventArgs> SurveyCompleted;
public void CompleteSurvey(Guid instanceId, string employee, string surveyResponse)
{
// the host will call this method when it wants to raisethe event into the workflow.
// Note that the workflow instance id needs to be passed in.
EventHandler<SurveyEventArgs> surveyCompleted = this.SurveyCompleted;
if(surveyCompleted != null)
{
surveyCompleted(null, new SurveyEventArgs(instanceId, employee, surveyResponse));
}
}
}
}
現(xiàn)在,將ExternalDataExchange服務(wù)添加到運(yùn)行時(shí),并且將接口實(shí)現(xiàn)的一個(gè)實(shí)例添加為本地服務(wù)。使用OnWorkflowIdled事件發(fā)送響應(yīng)給宿主。
using System;using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Activities;
namespace ExternalEventSample
{
class Program
{
static SurveyResponseService surveyService;
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
// add the local service to the external data exchange service
surveyService = new SurveyResponseService();
ExternalDataExchangeService dataService = new ExternalDataExchangeService();
workflowRuntime.AddService(dataService);
dataService.AddService(surveyService);
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
{ waitHandle.Set(); };
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
workflowRuntime.WorkflowIdled += OnWorkflowIdled;
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof
WorkflowConsoleApplication13.Workflow1));
instance.Start();
waitHandle.WaitOne();
}
}
static void OnWorkflowIdled(object sender, WorkflowEventArgs e)
{
surveyService.CompleteSurvey(e.WorkflowInstance.InstanceId, "Matt", "Very Statisfied");
}
}
}
回到工作流,將CallExternalMethod活動(dòng)拖到設(shè)計(jì)窗口。注意智能標(biāo)簽驗(yàn)證表明接口類(lèi)型和方法名稱(chēng)都沒(méi)有定義。點(diǎn)擊InterfaceType屬性會(huì)彈出一個(gè)標(biāo)準(zhǔn)的類(lèi)型瀏覽窗口以選擇合適的接口。方法可以從MethodName屬性的下拉列表中選擇,方法選擇好后,對(duì)應(yīng)于輸入?yún)?shù)和接口里定義的輸出的其他屬性就會(huì)添加到屬性窗口里。
當(dāng)CallExternalMethod活動(dòng)執(zhí)行時(shí),它通過(guò)ActivityExecutionContext.GetService獲得對(duì)契約實(shí)現(xiàn)的訪問(wèn),并調(diào)用其方法。
為使用HandleExternalEvent活動(dòng),要做的第一件事就是為宿主程序提供一種為工作流運(yùn)行時(shí)觸發(fā)事件的方法,以接收和路由消息。在上例中通過(guò)調(diào)用服務(wù)實(shí)現(xiàn)的方法CompleteSurvey方法來(lái)完成。該方法將觸發(fā)一個(gè)事件,運(yùn)行時(shí)會(huì)根據(jù)傳入的workflowId參數(shù)將其路由給相應(yīng)的工作流實(shí)例。在內(nèi)部實(shí)現(xiàn)時(shí),HandleExternalEvent會(huì)創(chuàng)建一個(gè)隊(duì)列并訂閱放在隊(duì)列里的消息。當(dāng)接收到一個(gè)事件時(shí),運(yùn)行時(shí)會(huì)將消息放在正在等待該類(lèi)型消息的隊(duì)列上。可以讓多個(gè)隊(duì)列同時(shí)監(jiān)聽(tīng)同一種類(lèi)型消息--如等待多個(gè)員工完成調(diào)查。在這種情況下,事件將被路由到哪個(gè)隊(duì)列的粒度可通過(guò)使用correlation來(lái)指定。
為使用HandleExternalEvent,首先將它拖到設(shè)計(jì)窗口里,其過(guò)程與CallExternalMethod類(lèi)似。當(dāng)工作流到達(dá)HandleExternalEvent活動(dòng)時(shí),它會(huì)創(chuàng)建一個(gè)隊(duì)列來(lái)監(jiān)聽(tīng)指定類(lèi)型的事件類(lèi)型,然后,或者停下來(lái),或者繼續(xù)執(zhí)行其他被調(diào)度執(zhí)行的活動(dòng)。
轉(zhuǎn)載于:https://www.cnblogs.com/free722/archive/2011/05/23/2053580.html
總結(jié)
以上是生活随笔為你收集整理的WCF揭秘学习笔记(5):WF定制活动的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python 无序列表中第k大元素_查询
- 下一篇: c语言 方程改main的值_c语言mai