WCF揭秘学习笔记(5):WF定制活动
WF(Windows Workflow Foundation,Windows工作流基礎)為.NET提供了一種基于模型的、聲明方式的過程執行引擎,它改變了傳統的通過一行行編寫代碼來開發服務功能的方式。
WF包含三個核心組件:活動框架(activity framework)、運行時環境(runtime environment)、工作流設計器(workflow designer)。
WF不是什么
工作流這個詞在軟件開發領域和相關社區里已經被“濫用”了。所以弄清楚WF在這些流行的工作流概念中到底指的是哪一種就非常重要。
1. WF不是服務器,雖然可以將工作流功能集中起來然后通過服務器暴露給其他應用程序使用。
2. WF不是一種BPM(Business Process Management,業務流程管理)工具,雖然可以將WF作為工作流執行引擎來創建BPM工具。
3. WCF并不直接面向業務分析人員,雖然可以使用可重新承載的設計器向業務分析人員提供創建自己的工作流的工功能。WF非常靈活,它允許將此功能集成到分析人員熟悉的環境中。如果現有的設計器不能滿足要求,還可以創建定制的工作流設計器。
4. WF不是企業應用集成工具,雖然可以將第三方的系統功能封裝在活動中,然后將它們加到工作流里。
5. WF不是一個玩具。這可以在具有大量服務器集群的企業級規模環境里高性能地運作。它可以勝任企業級應用,SharePoint Server就是一個很好的應用實例。而WF本身并不是一個企業級應用,它只是一組開發套件。
WF不僅可以用在基于服務器的應用程序里,它還可以用在WinForm應用中執行從服務協調到用戶界面定制的任何應用程序邏輯。也可以用在Web應用程序中以管理流程狀態。總之,任何可以編寫.NET代碼的地方都可使用它作為提供邏輯的代碼。
活動
活動(activity)是WF的基本構建模塊。從復雜的執行邏輯到執行一個對SQL數據庫的更新,這些操作都被封裝到一個個獨立的工作單元,也就是活動中。活動是所有直接或間接繼承自System.Workflow.ComponentModel.Activity的類。活動具有兩個方面的行為:運行時行為(runtime behavior)和設計時行為(design-time behavior)。
運行時行為是活動在工作流中使用時執行的代碼。它可能是訪問一個Web服務或執行一塊代碼,也可能是協調子活動的執行。一個常被問及的問題是:WF的性能怎樣?在活動級別,答案很簡單:活動可以和在.NET程序集里相同代碼的執行速度一樣快。因為活動只是一個已經編譯好的.NET程序集,它包含從activity繼承的類。管理活動的生命周期需要額外的開支。
當包含活動的工作流被創建時,該活動就會被初始化并停留在Initialized(初始)狀態。也就是說,當CreateInstance方法被調用且數據流實例被創建時,所有的活動都將被初始化。服務在被調度執行時,其狀態會變成Executing(執行)。如果一切正常,活動會進入到Closed(關閉)狀態,也就是它的任務已完成,可以休息了。
活動可能會遇到錯誤,從而進入Faulting狀態而被關閉。活動也可能被其他的活動取消掉,從而進入Canceling狀態,然后再進入Closed狀態。最后,如果活動為其執行定義了回滾或補償操作,活動就會從Closed狀態激活并進入到Compensating狀態。由于某些原因,一個錯誤或一個取消處理都需要調用補償邏輯。
另外,活動的設計體驗對創建工作流是非常重要的。活動可能需要在設計窗口里有些個性化的表示,目的是為了告訴開發者可以一目了然地知道活動正在做什么。
開箱即用活動
隨WF一起發布的活動通常被稱為開箱即用活動(out of the box activity)。它們是一組基本的活動,可用來創建簡單的工作流和組建新的活動。下表給出了每個開箱即用活動的簡單介紹:
創建定制服務
工作流開發者可能經常需要創建定制活動。從封裝經常使用的方法到創建能為一種新的執行模式建模的復合活動,開發者在開始一個工作流項目時需要首先思考他需要什么樣的活動。隨著時間的推移,這些活動可被復用或被用來組建更高層次的活動,最后就像處理現在的普通對象一樣。
基礎
創建定制活動的最基本方法就是從System.Workflow.ComponentModel.Activity繼承一個類。這將為活動創建所有的基本機制,除了活動實現的實際邏輯。可通過重載Execute方法來實現實際的邏輯。如下所示:
public class BasicActivity : Activity{
public BasicActivity()
{
this.Name = "BasicActivity";
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
Console.WriteLine("Basic Activity");
return ActivityExecutionStatus.Closed;
}
}
Execute方法執行活動的操作并需同時通知運行時它的狀態。在這里,它返回Closed狀態,表示該活動已完成它的工作。一個更加復雜的模式是:返回Executing狀態,同時等待一個需要運行較長時間的工作完成。當這個較長時間的工作完成時,活動將通知運行時它已經完成了。在等待時,工作流實例可能停頓下來較長時間。
我們經常需要控制一些與活動相關的參數,它們會影響活動所提供的有用功能。最簡單的方法就是為活動類添加屬性,如下所示:
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;
}
當使用XAML聲明式地創建工作流時,這些屬性會被作為活動的特性(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>
這樣就允許在設計器里設定屬性的值從而定制活動的行為。但這還是靜態的,屬性被限制為設計時設定的值。當創建的工作流需要評估傳入的定制對象時呢?這可以通過使用依賴屬性(dependency property)來實現。依賴屬性和之前介紹的.NET標準屬性類似,但在聲明和用戶法上有所不同。VS中有內建的代碼來創建它們,如下所示:
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);
}
}
這個稍長的聲明里不僅有屬性的聲明,而且還包含了一個靜態DependencyProperty變量的聲明。DependencyProperty是一種添加到DependencyObject(Activity就繼承自DependencyObject)的特殊屬性類型。DependencyProperty不同于傳統屬性,它可以支持三種特殊的使用方式:
1. 活動綁定。
2. 元數據,只在設計時賦值,在運行時不能更改。
3. 附加屬性(attached property),動態地為活動添加屬性。
使用依賴屬性的最普通場景是支持活動綁定。使用依賴屬性的好處是可以添加新的設計時行為。將活動拖到設計窗口后,觀察它的屬性窗口,會發現在剛長聲明的屬性旁邊新增一個new圖標。單擊該圖標會彈出一個新的Bind(綁定)對話框。
Bind對話框允許將屬性的值與工作流中的另外一個值動態綁定。這個值可以是在工作流創建時傳入該工作流的一個屬性,也可以是另外一個活動的屬性。在設計時,活動被告知到哪兒去查找依賴屬性的值。通過選擇工作流中同一類型(可以是一個定制類型)的另外一個值,一個綁定表達式會出現在屬性窗口里作為這個依賴屬性的值。表達式與下面的式子類似的:
Activity=Workflow1, Path=WorkflowOrderAmount
式子的第一部分表示源(這里是父工作流),然后是活動可以使用的屬性。式子里可以使用點操作符,如果希望被綁定的值是屬性的下面幾層,它也可以被訪問。依賴屬性的值還可以直接寫在代碼里。
Bind對話框里有一個Bind to a New Member(綁定到一個新成員)標簽頁。這個窗口可以使一個活動的依賴屬性“提升”(promote)為窗口活動(即包含活動的活動)的一個成員。
組合
第二種創建活動的方法是復合(composition),這也是VS的默認方法。以下代碼在VS中新建一個活動類的定義:
public partial class ACompositeActivity : SequenceActivity它繼承自SequenceActivity。SequenceAcitivity是創建順序執行工作流的基類,它的Execute方法負責將包含的活動依次調度執行。通過繼承,可保留我們希望的這種模式。這將使活動的開發者通過拖拽其他活動到新的活動里來創建一個新的活動,也就是從現有的活動創建新的活動。這是一個創建新工作單元的強大工具。這意味著,如果有一組已經實現且足夠強大的基本活動(如對現有API的封裝)的集合,就可使用它們快速創建更高層次的新功能單元。
考慮下面的活動:
1. SendEmail
2. LookupManager
3. WaitForResponseToEmail
使用這些活動及一些開箱即用活動提供的結構化活動,可以創建任意復雜的標準活動,然后新建的活動就可以作為批準活動被任意的工作流使用。
定制復合活動
我們還可以創建另外一類活動,即定制復合活動(custom composite activity)。復合活動包含子活動,它的Execute方法負責調度執行這些子活動。在開箱即用活動中,Sequence、While和Parallel都是復合活動的例子。
復合活動執行時,會執行它的子活動并訂閱這些子活動的完成事件。活動會返回Executing狀態,向運行時指明它要繼續執行下一個被調度的活動。通過接收子活動的完成事件,復合活動可以繼續調度執行其他的活動或判斷是否可以結束。當所有的活動都結束了或復合活動決定結束時,它會返回Closed狀態,表明它已經結束了。工作流會確保在允許一個活動關閉前,它的所有子活動都關閉了。
活動通信
工作流并不是在完全孤立的環境里執行的,它們經常需要和宿主程序交互以發送消息給宿主或從宿主接收消息。兩個開箱即用活動被設計成支持這樣的交互:Handle-ExtenalEvent和CallExternalMethod。這些活動通過宿主和工作流間共享的契約通信。該契約的實現作為本地服務提供給運行時ExternalDataExchangeService。
讓我們來看一個基于員工調查應用場景的例子。首先創建一個在宿主和工作流間共享的契約,并為其添加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;
}
}
}
該接口定義了一個事件、一個定制的事件參數類和一個公共方法。我們再提供一個接口的實現:
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));
}
}
}
}
現在,將ExternalDataExchange服務添加到運行時,并且將接口實現的一個實例添加為本地服務。使用OnWorkflowIdled事件發送響應給宿主。
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活動拖到設計窗口。注意智能標簽驗證表明接口類型和方法名稱都沒有定義。點擊InterfaceType屬性會彈出一個標準的類型瀏覽窗口以選擇合適的接口。方法可以從MethodName屬性的下拉列表中選擇,方法選擇好后,對應于輸入參數和接口里定義的輸出的其他屬性就會添加到屬性窗口里。
當CallExternalMethod活動執行時,它通過ActivityExecutionContext.GetService獲得對契約實現的訪問,并調用其方法。
為使用HandleExternalEvent活動,要做的第一件事就是為宿主程序提供一種為工作流運行時觸發事件的方法,以接收和路由消息。在上例中通過調用服務實現的方法CompleteSurvey方法來完成。該方法將觸發一個事件,運行時會根據傳入的workflowId參數將其路由給相應的工作流實例。在內部實現時,HandleExternalEvent會創建一個隊列并訂閱放在隊列里的消息。當接收到一個事件時,運行時會將消息放在正在等待該類型消息的隊列上。可以讓多個隊列同時監聽同一種類型消息--如等待多個員工完成調查。在這種情況下,事件將被路由到哪個隊列的粒度可通過使用correlation來指定。
為使用HandleExternalEvent,首先將它拖到設計窗口里,其過程與CallExternalMethod類似。當工作流到達HandleExternalEvent活動時,它會創建一個隊列來監聽指定類型的事件類型,然后,或者停下來,或者繼續執行其他被調度執行的活動。
轉載于:https://www.cnblogs.com/free722/archive/2011/05/23/2053580.html
總結
以上是生活随笔為你收集整理的WCF揭秘学习笔记(5):WF定制活动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 无序列表中第k大元素_查询
- 下一篇: c语言 方程改main的值_c语言mai