和我一起學Windows Workflow Foundation(微軟工作流)

mq0036發表於2011-04-20

(1)-----建立和除錯一個WF例項

今天開始,我打算開始學習WWF,從網上搜尋到了部分相關資料,也找到了一些文件和實驗。但是,資料以英文的佔多數,所以,在學習起來似乎比較吃力,不過相信我能堅持下來,順便提高點英語閱讀能力,不過本人英文水平實在是差,解釋的不到位或錯誤的地方請大家諒解(千萬別笑話我,哈哈)。畢竟我也是從空白開始學習WWF,讓我們一起進步。

首先,我們需要安裝WinFX下載)和Visual Studio 2005 extensions for .NET Framework 3.0 (Windows Workflow Foundation)。這是我們必備的開發元件。

這些例子和教程是從微軟的labs上下載到的,分為10個部分。先來學最基礎的第一部分先 :)

第一部分的目的

這個試驗的目的是介紹Windows Workflow Foundation的工作流概念的關鍵點

完成了這個試驗以後,我們可以學習到:

· 使用Visual Studio 2005 為WWF設計順序工作流

· 配置和使用Visual Studio 2005偵錯程式除錯你的工作流

· 讓工作流接受引數

· 通過接收事件觸發一個新的工作流例項

· 定義條件

· 為If/Else, Delay, Listen, and Code配置基本的活動(activities不知道怎麼翻譯好)

· 給工作流為接收到的事件定製活動

工作流是在一個活動圖中進行定義的,它可能是一個人為操作或一個系統程式。一個活動在工作流中是一個步驟,其體現為一個可執行單元,並且是可重用的,可整合的。工作流在設計好後被編譯成.net程式集,它被工作流執行時和通用語言執行時(CLR)所執行。

一.建立一個Hello World工作流

在這次練習裡,我們將通過VS2005工作流設計器設計一個非常簡單的“Hello World”工作流。這個Hello World工作流將通過很簡單的程式碼實現一個順序工作流,它將在我們的命令列中輸出“Hello, World!”。我們將瞭解工作流的定義和其程式碼。最後,我們將學習怎樣通過VS2005在我們的機器上執行、除錯我們的工作流。

開啟VS 2005,新建專案,選擇“順序工作流控制檯應用程式”,修改專案名稱為:HelloWorldWorkflow.

 

1. 點選確定我們將生成一個新的工作流專案。

2. 這個專案會為我們自動生成一個工作流模版Workflow1。在這裡我們是不需要這個模版的,我們只是做一個非常簡單的例子,所以我們刪除這個Workflow1.cs檔案。

3. 右鍵單擊專案選擇新增新項

4. 選擇“順序工作流(具有單獨的程式碼)”,點確定新增。這裡將新增兩個檔案Workflow1.xoml和Workflow1.xoml.cs,Workflow1.xoml包含了描述工作流型別的XML語言,而其CS字尾的的檔案則包含這個工作流內的動作執行程式碼。

5. 下面我們要做的就是新增程式碼了。雙擊Workflow1.xoml檔案來開啟VS 工作流設計器檢視。

6. 我們從工具箱裡可以砍刀我們可以新增的各種活動。

7. 我們從工具箱中拖拽“Code”這個活動到我們的工作流當中。

8. 注意,這個時候在我們拖拽進來的“Code”這個活動的右上角有一個紅色的歎號,點選下拉的小三角我們可以看到顯示這個歎號的呀,是因為屬性ExecuteCode還沒有被設定。

9. 那麼下一步很明白了,我們需要設定ExecuteCode,點選這個提示,我們會發下在屬性視窗的ExecuteCode會顯示高亮,在其內輸入codeActivity1_CodeHandler並雙擊,會在後臺程式碼Workflow1.xoml.cs中生成codeActivity1_CodeHandler方法

10. 可以看到,這個類繼承自 SequentialWorkflowActivity 基類. 我們要在方法中輸入:

Console.WriteLine("Hello, World!");

全部程式碼如下:

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
namespace HelloWorldWorkflow
{
public
			partial
			class Workflow1 : SequentialWorkflowActivity
{
private
			void codeActivity1_CodeHandler(object sender, EventArgs e)
{
Console.WriteLine("Hello, World!");
}
}
}

11. 按Ctrl+F5執行程式,如果出現:
則說明成功。

12. 是不是太簡單了?至少,我們第一次的工作流編寫完成了。

13. 等等,別急,還有個好玩的,那就是WWF的除錯功能。我們在以前除錯的時候,是要在程式碼中設定斷點的,現在,你可以試一下除錯WWF。

14. 開啟工作流的設計檢視Workflow1.xoml。

15. 選中我們剛才拖拽過來的codeActivity1,點右鍵,選斷點—插入斷點(或者選中後直接點F9),看到了什麼?

6. 現在我們再按F5,在執行到這裡的時候就會停止,可以進行除錯了。

7. 點F11單步執行將進入我們的codeActivity1_CodeHandler事件,是不是很方便?

  (2)-----讓WF通過引數接收資料

在這一節,我們一起學習通過其他程式呼叫工作流,並且在工作流啟動時給工作流傳遞一些資料,使其在工作流中使用。

在workflow開始執行時,我們有兩種方式給工作流傳遞資料------引數和事件。今天,我們先說說怎樣通過引數來給工作流傳遞資料。

需要傳遞給workflow的引數,我們需要先在工作流中通過屬性來進行設定,而後在其他程式呼叫這個工作流建立一個工作流的例項時,通過 Dictionary<string, object>鍵值集合進行傳遞,如下:

繼續使用我們上一節用到的程式碼,給Workflow1 類新增兩個屬性FirstName和LastName

 

        private string firstName ;

        
public string FirstName
        {
            
get
            {
                
return firstName;
            }
            
set
            {
                firstName 
= value;
            }
        }

        
private string lastName;

        
public string LastName
        {
            
get
            {
                
return lastName;
            }
            
set
            {
                lastName 
= value;
            }
        }

 

這裡我們將通過其他應用程式傳遞fristName和lastName並讓這兩個值在工作流執行過程中以對話方塊的形式顯示出來。

因為我們上一節中建立的是個控制檯應用程式,所以讓起彈出對話方塊需要應用System.Windows.Forms程式集。

然後修改我們的Workflow1類中的codeActivity1_CodeHandler

        private void codeActivity1_CodeHandler(object sender, EventArgs e)
        {
            System.Windows.Forms.MessageBox.Show(
                    
"Hello world: " + firstName + " " + lastName);
        }


現在我們已經修改好我們要做的工作流程式碼了。

 

下面我們要建立一個winForm應用程式。

新增一個新的專案WinFormTestHost

接下來我們要確認我們新建立的專案WinFormTestHost能夠呼叫工作流Workflow1。

給WinFormTestHost新增引用,引用專案HelloWorldWorkflow.

同時,我們還要讓我們的這個winfrom應用程式引用wwf程式集。繼續新增引用,選擇.net選項卡,新增如下三項:

System.Workflow.Activities

System.Workflow.ComponentModel

System.Workflow.Runtime

 

修改我們的Form1窗體。

新增兩個Label分別為Label1和Label2,Text屬性分別為First name和 LastName。

新增兩個TextBox分別為txtFirstName和txtLastName。

新增一個按鈕btnStartWorkflow,用來啟動我們的工作流,Name屬性為btnStartWorkflow,Text屬性為Start Workflow。

這個簡單的窗體就做完了,我們來新增程式碼。

首先在我們的類Form1.cs中新增工作流執行時宣告:

       private WorkflowRuntime wr;



雙擊btnStartWorkflow新增事件處理程式。

        private void btnStartWorkflow_Click(object sender, EventArgs e)
        {
            
if (wr == null)
            {
                wr 
= new WorkflowRuntime();
                wr.StartRuntime();
            }

            
//定義鍵和值的集合(Dictionary)用來傳遞引數
            Dictionary<stringobject> parameters = new Dictionary<stringobject>();
            parameters.Add(
"FirstName", txtFirstName.Text);
            parameters.Add(
"LastName", txtLastName.Text);

            
//建立一個工作流例項
            WorkflowInstance instance = wr.CreateWorkflow(typeof(HelloWorldWorkflow.Workflow1), parameters);
            
//啟動工作流
            instance.Start();
            
        }

最後給FormClosed事件新增一段程式碼,使其在關閉窗體時關閉工作流

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            
if (wr != null)
            {
                
if (wr.IsStarted)
                {
                    wr.StopRuntime();
                }
            }
        }

 

現在,我們的程式就開發完成了,點選F5,將開啟窗體:

我們輸入名稱後點Start Workflow,將彈出窗體:

這個窗體是在Workflow1中執行的,同時,我們也看到,在winfrom窗體中輸入的值成功的傳遞到了工作流中。

 (3)-----使用If/Else活動,定製活動處理工作流,使用事件傳遞資料

上一節我們學習了怎樣通過引數傳遞資料,這節我們就說說通過事件傳遞資料的方法,由於這節除了我們自己寫的一部分程式碼外,還需要用到另外需要觸發事件的ExpenseApplication外部應用程式和所用事件示例程式碼,所以請下載示例程式原始碼,根據需要新增專案的引用。

另外,我們將學習到怎樣使用if/else 根據條件處理工作流 ,還將學習根據定製活動有條件的處理工作流。我們將在製作一個簡單的處理費用支出報表的工作流中學習到這些概念。當工作流在報表資料提交的時候將接收到一個事件,這個事件的引數將把包含實際的費用支出報表資料提交到程式。如果報表的支出費用少於1000,工作流將自動提交,否則,將自動拒絕。

首先,建立一個名稱為ExpenseWorkflows的空工作流專案。

新增一個含有單獨程式碼的順序工作流。預設名稱就叫Workflow1.xoml

我們將使用HandleExternalEventCallExternalMethod 這兩個活動來和支出報表建立聯絡,在這之前,先給我們的解決方案新增一個現有專案ExpenseLocalServices(從下載的原始碼中可以找到這個專案),然後給我們的專案ExpenseWorkflows新增一個引用,引用這個現有專案。

新增提交支出報表的活動

1. 在Workflow1這個類中我們需要新增兩個成員變數供將來使用,一個變數名稱為reportArgs,用於接收報表提交這個事件的引數物件。另外一個變數將儲存報表的支出費用。

(Snippet: Lab01_Ex03_Task03_MemberVariables)

public partial class Workflow1 : SequentialWorkflowActivity

{

public ExpenseLocalServices.ExpenseReportSubmittedEventArgs reportArgs =

default(ExpenseLocalServices.ExpenseReportSubmittedEventArgs);

public int amount = default(System.Int32);

}

2. 開啟workflow1.xoml的設計檢視。

3. 從工具箱中找到HandleExternalEvent 這個活動拖拽到我們的工作流設計檢視中,將其Name屬性設定為expenseReportSubmitted1如圖:

4. 點選紅色的歎號選擇Property ‘InterfaceType’ is not set.來設定InterfaceType屬性。

5. 點選[…]按鈕將出現 .NET Type Browser 對話方塊。從引用程式集目錄樹中選擇 ExpenseLocalServices.IExpenseService 。如下圖所示:

6.現在我們的工作流設計檢視expenseReportSubmitted1上還有一個歎號,是因為我們的EventName還沒有設定。同樣點選它,設定EventName屬性。點選下拉選單框,選擇ExpenseReportSubmitted。

7.繼續設定expenseReportSubmitted1的屬性,找到e這個屬性,點[…]按鈕,選擇Bind to an existing member選項卡下的Workflow1---reportArgs.也就是把這個提交事件的e引數和我們定義的成員變數reportArgs繫結。

8.雙擊設計檢視中的expenseReportSubmitted1建立Invoked事件。

(Snippet: Lab01_Ex03_Task03_ReportSubmittedInvoked)

public void ReportSubmitted_Invoked(object sender, EventArgs e)

{

Console.WriteLine("ReportSubmitted_Invoked");

//給我們的成員變數amount設定事件觸發得到的值

this.amount = this.reportArgs.Report.Amount;

}

新增自動審批活動到我們的工作流

1. 我們將新增一個定製活動(我們還沒學到怎麼定製活動,所以先直接引用做好的定製活動吧,在下載的原始碼裡的ExpenseActivities,可以看一下,其實很容易理解,就象我們做定製控制元件。:))來判斷所做的提交是否進行自動審批。

2. 單擊工具|選擇工具箱項

3. 選擇 .NET Framework元件 選項卡並單擊瀏覽

4. 導航到我們程式碼原始檔的ExpenseWorkflows/ExpenseActivities/bin/Debug/ExpenseActivities.dll.將其新增。

5. 現在我們的工具箱會多一個新的選項卡叫做ExpenseActivities,裡面有兩個新的活動:AutoApproveGetManager

6. 現在把我們把一個新的定製活動AutoApprove拖拽到我們的工作流設計檢視。

7. 現在我們的工作流設計檢視有兩個活動,如圖:

8. 在設計檢視中選擇活動autoApprove1 ,設定其Amount屬性,繫結到Workflow1.amount

給工作流新增條件

1. 從工具箱中找到IfElse活動拖拽到設計檢視中autoApprove1的下面。

2. 將其(Name)屬性設定為EvaluateExpenseReport

3. 選擇IfElse左邊活動分支設定:(Name屬性設定為IfAutoApproveReport;Condition屬性設定為Declaritive Rule Condition

4. 展開Condition的“+”,設定其中的ConditionName屬性為AutoApproveCondition

5. 對Expression屬性編輯表示式:this.autoApprove1.Approved。如圖:

6. 選擇EvaluateExpenseReport右邊的活動分支將其(Name)屬性改為ElseRejected.

給支出報表是否通過新增邏輯判斷

1. 從工具箱中選擇CallExternalMethod活動新增到標籤為IfAutoApproveReport的條件分支裡面,設定這個活動(Name)屬性為approveExpenseReport1

2. 象上面我們設定HandleExternalEvent活動時一樣,我們把InterfaceType屬性按照相同的方法設定成ExpenseLocalService.IExpenseService

3. MethodName屬性選擇下拉選單框中的ApproveExpenseReport方法。

4. 同樣的,我們還要設定properties屬性,選擇report,設定如下所示:

5. 現在,左邊的IfAutoApproveReport條件分支我們配置完畢了。接下來配置右邊的條件分支ElseRejected

6. 新增另一個CallExternalMethod活動到ElseRejected,設定Name屬性為rejectExpenseReport1

7. 選擇InterfaceType屬性設定為:ExpenseLocalService.IExpenseService

8. 選擇MethodName屬性設定為:RejectExpenseReport

9. 選擇Report屬性繫結到:reportArgs.Report.

10. 現在我們的工作流設計檢視如圖所示:

除錯工作流

到現在,我們的工作流就做完了。接下來我們要除錯它,看他的執行過程。

不過,這裡我們還需要藉助兩個專案,來觸發我們所做的工作流事件。

1.進入ExpenseWorkflows/ExpenseHost/bin/Debug目錄,複製ExpenseHost.exeExpenseHost.exe.config到我們的專案檔案目錄:ExpenseWorkflows/ExpenseWorkflows/bin/Debug。

還有ExpenseWorkflows/ExpenseApplication/這個專案(一會除錯用)。

2. 現在需要配置我們的ExpenseWorkflows專案,將其設定為啟動專案,然後右擊專案選擇屬性,選擇除錯,在啟動操作選擇“啟動外部程式”,瀏覽到ExpenseWorkflows/ExpenseWorkflows/bin/Debug/ExpenseHost.exe

3. 現在可以按F5啟動除錯了,當然,為了看整個專案的執行過程,我們可以給各個工作流和其他感興趣的位置設定斷點來詳細學習工作流的執行過程。

Task 7 – Test the Expense Reporting Workflow

測試支出報表工作流

1. Compile and run the ExpenseWorkflows project under the Visual Studio debugger by pressing F5 or selecting the Debug | Start Debugging menu command.

2. 點F5啟動除錯,將顯示如下介面,執行ExpenseHost命令列程式。

4. 執行 ExpenseApplication.exe (在如下目錄:ExpenseWorkflows/ExpenseApplication/bin/Debug)。

我們提交一條報表資料,金額填寫了1500,

5. 在VS2005給活動 expenseReportSubmitted1設定斷點

6. 然後單步執行可以看到 ReportSubmitted_Invoked 事件處理的程式碼.

7. 繼續單步執行到活動 rejectExpenseReport1

8. 看到我們的支出報表請求被拒絕了,這裡會呼叫方法我們設定的RejectExpenseReport來更新剛才我們的那條report記錄,將起狀態設定為拒絕。

9. 我們可以看到應用程式狀態

10. 點選Refresh Reports 

 變成了拒絕(Rejected)。

11. 如果輸入小於1000的數,會看到

12. 狀態為通過。

 

大家可以仔細閱讀一下這些專案的程式碼,然後除錯一遍,應該就能瞭解其中工作的過程和工作流中HandleExternalEventActivity和CallExternalMethodActivity的使用方法了。

(4)-----使用Listen,Delay,和其他envnt-based定製活動

這一節我們將擴充上一節制作的支出報表的例子(所以繼續使用第三個試驗使用的例子),給他新增一個功能使管理者可以去管理大於1000的金額是通過稽核還是拒絕。我們將使用一些新的活動包括 Listen, Delay等,另外還有一些定製的活動。

程式碼點選這裡下載

新增一個請求管理者通過的活動

1. 首先我們需要在類Workflow1宣告ReportEmployeeId, ManagerEmployeeIdreviewArgs3個變數,所以給workflow1.xoml.cs新增如下程式碼:

public string ReportEmployeeId = default(System.String);

public string ManagerEmployeeId = default(System.String);

public ExpenseLocalServices.ExpenseReportReviewedEventArgs reviewArgs =

default(ExpenseLocalServices.ExpenseReportReviewedEventArgs);

2. 開啟Workflow1.xoml的設計檢視,選擇EvaluateExpenseReport這個IfElse活動右邊的活動分支(ElseRejected),修改其Name屬性為ElseManagerApproval。刪除裡面我們在上一節中建立的活動rejectExpenseReport1

3. 新增一個自定義活動GetManager(在我們上一節新增的自定義活動ExpenseActivities.dll中)到IfElse活動分支的ElseManagerApproval節點內,如圖:

4. 設定getManager1的屬性:
ManagerEmployeeId –點選[…]繫結到ManagerEmployeeId

ReportEmployeeId –點選[…]繫結到ReportEmployeeId

5. 修改Workflow1類中的ReportSubmitted_Invoked的程式碼,新增高亮的那行,將引數提供者提供的EmployeeId賦值給剛才我們宣告的變數ReportEmployeeId。

6. Change the code in the ReportSubmitted_Invoked method to set the ReportEmployeeId to the value provided in the Expense Report’s EmployeeId property. Add the highlighted line of code below.

private void expenseReportSubmitted1_Invoked(object sender, ExternalDataEventArgs e)

{

Console.WriteLine("ReportSubmitted_Invoked");

this.amount = this.reportArgs.Report.Amount;

this.ReportEmployeeId = this.reportArgs.Report.EmployeeId;

}

7. 在工作流設計檢視中從工具箱拖拽一個活動CallExternalMethodgetManager1活動的下面。

8. 設定其(Name) 屬性為 requestManagerApproval1

9. InterfaceType屬性為ExpenseLocalServices.IExpenseService

10. MethodName 屬性從下拉選單框中選擇 RequestManagerApproval

11. 還要設定如下屬性:

12. ManagerEmployeeId – 設定為 ManagerEmployeeId.

13. Report – 設定為 reportArgs.Report.

14. 現在EvaluateExpenseReport 節點如下圖所示:

新增一個Listen活動來等待支出報表的稽核

1. 在requestManagerApproval1下新增一個Listen 活動。

2. 設定其(Name)屬性為ListenForManagerApproval,修改其eventDrivenActivity1eventDrivenActivity1的(Name)屬性分別為:ManagerReviewedReviewTimeout

3. 如圖所示:

4. 在ManagerReviewed部分新增一個HandleExternalEvent活動,將這個新新增的活動(Name)屬性設定為expenseReportReviewed1

5. 繼續給我們新建立的這個活動設定屬性,將

a. InterfaceType 屬性設定為ExpenseLocalServices.IExpenseService.

b. EventName 屬性設定為ExpenseReportReviewed

c. 繫結屬性ereviewArgs

6. 從工具箱中拖拽一個Delay活動到設計檢視中Listen活動下的ReviewTimeout分支。

7. 設定這個新新增的活動delayActivity1的屬性TimeoutDuration值為:00:00:30

8. 現在的工作流如圖所示:

批准或拒絕支出報表

1. 在expenseReportReviewed1活動下新增一個IfElse活動,修改(Name)屬性為EvaluateReview

2. 選擇左邊的分支,設定起屬性:

a. (Name) – 設定為 IfApproved

b. Condition – 選擇 CodeCondition,說明我們將通過程式碼控制條件。

c. 展開Condition屬性,在下一級Condition中輸入IfReportApproved_Condition,並雙擊,將在Workflow1類中自動新增此事件處理程式。

d. 我們輸入入下程式碼,表示如果通過則將條件引數值設定為通過:

public void IfReportApproved_Condition(object sender, ConditionalEventArgs e)

{

e.Result = this.reviewArgs.Review.Approved;

}

3. 選擇EvaluateReview活動右邊的分支設定其(Name) 屬性為ElseDeclined

4. 新增一個CallExternalMethod活動到EvaluateReview活動的IfApproved分支。

a. (Name) 屬性設定為 approveExpenseReport2

b. InterfaceType屬性設定為ExpenseLocalServices.IExpenseService

c. MethodName屬性設定為ApproveExpenseReport

d. report 屬性設定為reportArgs.Report

5. 同樣的,新增一個CallExternalMethod活動到EvaluateReview活動的ElseDeclined分支。

a. (Name) 屬性設定為 rejectExpenseReport1

b. InterfaceType屬性設定為ExpenseLocalServices.IExpenseService

c. MethodName屬性設定為RejectExpenseReport

d. report 屬性設定為reportArgs.Report

6. 現在如圖所示:

7. 在rejectExpenseReport1活動上右鍵點選複製,貼上到delayActivity1活動下面並修改這個新建立活動的(Name) 屬性為 rejectExpenseReport2.

8. 如圖:

9.

10. 現在我們的工作流製作完成了,他的全圖如下所示

測試報表支出工作流

1. Next you will verify that the startup application is correct.

2. 察看ExpenseWorkflows專案的屬性,點選除錯選項卡,象上一節一樣,確認在啟動操作中選擇啟動外部程式,並導航到ExpenseWorkflows/ExpenseWorkflows/bin/Debug/ExpenseHost.exe。

3. 下面點選F5啟動除錯。

4. 執行ExpenseWorkflows/ExpenseApplication/bin/Debug目錄下的ExpenseApplication.exe

5. 執行ExpenseWorkflows/ManagerApplication/bin/Debug 目錄下的ManagerApplication.exe

6. 如圖

7. 現在,我們再在Expense Reporting 中提交一條資料:

8. 在Manager Expense Report Review中通過點選重新整理獲得此條資料:

9. 並且,通過點選通過還是拒絕來稽核此條紀錄是否通過。如果超時30秒,那麼就會自動拒絕。

10. 最終提交的資料如下:

11.

12.

這裡我們可以學到Listen和Delay的用法,更充實了第三個試驗的內容。

(5)-----使用activity設計器建立一個整合的定製activity

Lab01已經完了,lab02中說的是建立自定義的Activity

Activities是工作流模型的一個可執行單元,它可以被重用,也可以把多個Activities整合成一個Activities。一個工作流可以由多個Activities組成,一個Activities也可以由其他的Activities構成,最終,每個Activities都作為一個可執行單元的形式表現出來。這次,我們來學習建立一個簡單的Activity。

Activities是一個其實類,我們可以為其編寫屬性,方法和事件----這些屬性方法和事件引用自workflow。我們還可以建立Activity並部署到.net程式集中來重用。

先看第一個:

使用activity設計器建立一個合成的定製activity

在這個練習裡,我們將建立一個有兩個分開的程式碼段組成的activity,然後我們將在一個工作流專案中使用這個activity,執行這個工作流察看執行結果。

建立一個新的WF專案

1. 建立一個名稱為CompositeActivitySample順序工作流控制檯應用程式。

2.

3. 開啟Program.cs檢視其程式碼。

4. Program.csMain()方法包含了啟動,載入和執行和等待工作流完成的示例程式碼。

5. 將專案中的Workflow1.cs重新命名為HelloWorldWorkflow.cs並在彈出的提示對話方塊中選擇時,修改所有程式碼引用。

建立一個Activity專案

現在我們已經建立了一個工作流專案,我們接下來要建立一個Activity專案來新增到解決方案。

在這個專案中我們去定製一個新的activity,這個新的activity中我們去新增兩個基本的activity --- code的來整合成我們定製的新的activity 。

1. 新建一個專案,選擇“工作流Activity庫”,名稱為HelloWorldActivityLibrary

2. 重新命名Activity1.cs為HelloWorldActivity.cs。同樣在彈出的是否對起所有引用的專案執行重名名提示時選擇是。

3. 開啟HelloWorldActivity的設計檢視。

4. 從工具箱中拖拽一個Code activity到我們的設計檢視中。

5. 如圖:

6. 修改起(Name)屬性為writeHello

7. 雙擊設計檢視中的writeHello activity建立一個writeHello_ExecuteCode方法。

8. 在writeHello_ExecuteCode 方法中輸入如下程式碼:

Console.Write("Hello, ");

9. 現在我們回到設計檢視,再拖拽第二個Code activity到我們的設計檢視中writeHello的下面,並修改(Name)屬性為writeWorld

10. 雙擊設計檢視中的writeWorld activity建立一個writeWorld _ExecuteCode方法。

11. 在 writeWorld _ExecuteCode 方法中輸入如下程式碼:

Console.WriteLine("World");

 

生成工作流解決方案

現在我們將剛剛建立的這個activity新增到工作流中,並執行察看效果。

1. 我們點“生成---生成解決方案”或者直接點F6來生成解決方案。

2. 選擇CompositeActivitySample專案中的HelloWorldWorkflow.cs來開啟設計檢視。

3. 這時,我們可以看到工具箱中會出現新的元件欄:

4.

5. 拖拽HelloWorldActivity到我們HelloWorldWorkflow的工作流設計檢視中,並且把這個activity的(Name)屬性修改為helloWorld

6. 現在我們把CompositeActivitySample作為啟動專案按ctrl+f5檢視執行結果吧。

7. .

8. 這裡我們建立的這個activity按照順序工作流的形式進行了執行。列印出“Hello,World”

9. 現在我們應該已經知道怎樣建立一個簡單的合成的activity了吧 :)

(6)-----製作一個基本的定製活動

上一節,我們的自定義活動其實是“拼”出來的,這次,我們來做一個基本的定製活動(不再是將已有的活動進行拼湊),我們將通過自定義屬性來設定一封email的資訊。我們將給建立給自定義活動建立一個執行元件,並把這個自定義活動新增到工作流中。這個執行元件將使用.NET API來把包含資訊的 email傳送出去。

建立一個工作流專案

1. 建立一個順序工作流控制檯應用程式,名稱為CustomPropertySample

2. 重新命名Workflow1.cs的名稱為SendMailWorkflow.cs

建立一個工作流Activity專案

1. 新增新專案 工作流Activity庫,名稱為SendMailActivityLibrary記得勾上新增到解決方案的勾哦)。

2. 修改Activity1.cs為SendMailActivity.cs

3. 單擊活動設計檢視中的標題(或者雙擊SendMailActivity.cs),可以看到如下介面:

4. 在屬性視窗,選擇Base Class 屬性,點選“…”開啟瀏覽對話方塊來為這個活動選擇一個基類。

5. 選擇System.Workflow.ComponentModel名稱空間下的Activity後點選OK。這樣我們自定義的工作流活動是繼承自基本的活動,而不是像以前一樣繼承自類似System.Workflow.Activities.SequenceActivity來拼湊工作流了。

6. 右鍵點選SendMailActivity.cs 選擇檢視程式碼。

7. 在如圖所示處點選右鍵,選擇插入程式碼段。

8. 選擇Workflow。

9. 選擇 DependencyProperty - Property.

10. 此時將插入一些模板程式碼在我們選擇的位置。

下面是一些我們的屬性(property)程式碼段中可以被新增的屬性(attribute)描述:

 

Name

Type

Description

Browseable

Boolean

Indicates whether this property appears in the Properties window.

指出這個屬性在我們自定義活動右側的屬性欄中是否可見

Category

String

A user-defined category for the property.

使用者為這個屬性自定義的分類

Description

String

A description for the property.

此屬性的描述

DesignerSerializationVisibility

Visible, Hidden, or Content

Determines how and if properties will be serialized.

Visible (the default) – the property will be serialized normally.

Hidden – prevents property serialization

Content – used for collection properties. The collection object itself is not serialized, however the contents of the collection are.

If a developer chooses a collection type, this property will be set to Content.  If a developer chooses a non-serializable type, this property will be set to Hidden.

ValidationVisibility

Optional, Required, or Hidden

Specifies how the property’s value is validated.

Optional (the default) – the property can accept null values.

Required – The property must be set to a non-null value and is checked to ensure that this is the case.

Hidden – There is no automatic validation of the property’s value.

If ValidationVisibility is set to ‘Required,’ when the component is reused, it will require the property to be configured via smart tags, obviating the need for a check in the activity’s Validator class.

 

如圖:

12. 類似的,新增如下屬性:

Name

Type

Description

Category

From

string

From Email Address

EmailActivity

Subject

string

Subject of Email

EmailActivity

Body

string

Body of Email

EmailActivity

到這裡我們的這個活動應該有4個屬性了。.

 

構建工作流解決方案

1. 生成解決方案.
2. 然後會發現SendMailActivity會被新增到工具欄(在解決方案資源管理器中雙擊SendMailWorkflow.cs,就可以看到工具欄出現了SendMailActivity)。

3. 從工具欄中將 SendMailActivity 活動拖拽到 Visual Studio 工作流設計器中,並修改 (Name) 屬性 為 sendMail.

4. 注意,現在我們可以看到我們在自定義活動那個專案中建立的屬性出現在了屬性視窗:

5. 現在可以為這個活動的自定義屬性設定初始值值。

新增一個可執行元件

1. 右鍵點選 SendMailActivity.cs 選擇檢視程式碼

2. 在程式碼檔案底部重寫活動的 Execute方法,新增如下程式碼:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) 



Console.WriteLine(To); 

return ActivityExecutionStatus.Closed; 

}
 

 

構建專案

1. 選擇重新生成解決方案。

2. 在解決方案資源管理器中雙擊 SendMailWorkflow.cs 檢視工作流設計檢視.

3. 選擇除錯-開始執行(不除錯)來執行專案(或點ctrl+F5),可以看到命令列的輸出資訊。

4. 命令列中將顯示我們的郵件將寄出的地址:

更新這個活動,使他能夠傳送郵件

現在我們已經吧上面自定義的活動製作成功了,能供通過輸出我們自定義的屬性到命令列中。現在,我們修改這個程式碼,使他真的可以傳送郵件。

1. 首先,我們本機的新增刪除windows元件中確認已經安裝了SMTP服務

2. 回到 SendMailActivity.cs 檔案的程式碼檢視。

3. 新增如下引用(用以傳送郵件):

using System.Net;

using System.Net.Mail;

4. 用如下程式碼替代我們Execute方法中的測試程式碼---用來傳送郵件:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) 



MailAddress toAddress 
= new MailAddress(To); 

MailAddress fromAddress 
= new MailAddress(From); 

MailAddressCollection addresses 
= new MailAddressCollection(); 

addresses.Add(toAddress); 

MailMessage msg 
= new MailMessage(fromAddress, toAddress); 

msg.Subject 
= Subject; 

msg.Body 
= Body; 

SmtpClient mail 
= new SmtpClient("smtp.163.com"); 

mail.Credentials 
= new NetworkCredential("shinji329""123456"); 

mail.Send(msg); 

return ActivityExecutionStatus.Closed; 

}
 

如果出現異常,可以看一下是不是防毒軟體監控防止蠕蟲病毒的設定拒絕了我們的傳送。

在執行這個工作流專案偶,我們可以收到郵件:

現在,我們的自定義基本活動也做出來了,嘿嘿,比較簡單的說。

(7)-----給定製活動新增有效性驗證

給定製活動屬性新增有效性驗證

這個實驗也沒什麼可詳細說的了,無非就是給我們之前傳送E-Mail的活動的屬性里加個驗證,這裡我們驗證了E-mail的To和From屬性是否符合郵件的格式。如果不是正確的郵件格式,我們則在編譯的時候不允許進行編譯。

下面大概過一下:

1. 開啟我們上次的那個定製活動專案,在 SendMailActivityLibrary這個專案上新增一個新的程式碼檔案,名稱叫做ParametersValidator.cs

2. 在程式碼檔案中輸入如下程式碼,程式碼繼承自ActivityValidator,如果是合成的活動(就是上上次我們拼出來的那個自定義活動),那麼程式碼應該繼承自CompositeActivityValidatorActivityValidator在設計和執行時狀態會進行驗證,這些驗證依賴於活動,在我們進行編譯和執行時活動時,會自動執行這些程式碼檢視我們的屬性是否符合規則。

using System;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Text.RegularExpressions;
using System.Net.Mail;
namespace SendMailActivityLibrary
{
public
			class ParametersValidator : ActivityValidator
{
}
}

3. 重寫ValidateProperties方法來完成我們的驗證。活動的ValidateProperties方法是在進行編譯時執行的驗證方法。這些驗證語句將驗證我們輸入的這些屬性是否是正確的E-mail格式

public
			override ValidationErrorCollection ValidateProperties(ValidationManager manager, object obj)
{
ValidationErrorCollection validationErrors = new ValidationErrorCollection(base.ValidateProperties (manager, obj));
SendMailActivity sendMailActivityToBeValidated = obj as SendMailActivity;
if (sendMailActivityToBeValidated == null)
{
throw
			new InvalidOperationException("Parameter obj is not of type SendMailActivity");
}
if (!IsValidEmailAddress(sendMailActivityToBeValidated.To))
{
ValidationError CustomActivityValidationError =
new ValidationError(String.Format("/'{0}/' is an Invalid destination e-mail address", sendMailActivityToBeValidated.To), 1);
validationErrors.Add(CustomActivityValidationError);
}
if (!IsValidEmailAddress(sendMailActivityToBeValidated.From))
{
ValidationError CustomActivityValidationError =
new ValidationError(String.Format("/'{0}/' is an Invalid source e-mail address", sendMailActivityToBeValidated.From), 1);
validationErrors.Add(CustomActivityValidationError);
}
return validationErrors;
}
public Boolean IsValidEmailAddress(String address)
{
// must only proceed with validation if we have data    
			// to validate
			if (address == null || address.Length == 0)
return
			true;
Regex rx = new Regex(@"[^A-Za-z0-9@/-_.]", RegexOptions.Compiled);
MatchCollection matches = rx.Matches(address);
if (matches.Count > 0)
return
			false;
// Must have an '@' character
			int i = address.IndexOf('@');
// Must be at least three chars after the @
			if (i <= 0 || i >= address.Length - 3)
return
			false;
// Must only be one '@' character
			if (address.IndexOf('@', i + 1) >= 0)
return
			false;
// Find the last . in the address
			int j = address.LastIndexOf('.');
// The dot can't be before or immediately after the @ char
			if (j >= 0 && j <= i + 1)
return
			false;
return
			true;
}

4. 修改 SendMailActivity.cs 的程式碼

5. 新增 ActivityValidator 屬性到 SendMailActivity, 如下面所示:

namespace SendMailActivityLibrary

{

[ActivityValidator(typeof(ParametersValidator))]

public partial class SendMailActivity: System.Workflow.ComponentModel.Activity

{

6. 重新進行編譯.

7. 下面我們進行測試,修改SendMailWorkflow.cs 中的SendMailActivity活動,修改他的From屬性,讓他不是一個正確的E-mail格式,如:shinji3292163.com。

8. 重新進行編譯。

9. 可以看到如下介面:

10. 修改成正常的,就可以編譯成功了。

相關文章