UML软件工程组织

 

 

Windows Workflow Foundation之旅(一)
 
作者:wolf's cave 文章来源:CSDN
 

大学时,因为毕业论文的关系,开始接触workflow,后来断断续续的关注着这个的发展。前几天dudu的一篇WWF(Windows Workflow Foundation)的简介[翻译] 又勾起了我对workflow的兴趣。看到园子里有这么多关注wwf的朋友们,又发现网上鲜有关于wwf的中文资料,决定挤出点时间把wwf的doc给整个翻译过来,给园子里跟我一样洋文不怎么行的兄弟们点便利。

小弟才疏,又是第一次写blog,大家见谅了~。为尽量保持原意,我就有什么翻译什么了……那就开始吧~

Windows Workflow Foundation之旅 (一) - 概述

翻译自--ms-help://MS.WinWF.v1.EN/WinWF_GettingStarted/html/55da2060-f10f-4ef4-a923-b38e4504516b.htm

组织并执行一系列的操作或者活动的最自然的方式——那就是工作流——同时也是构造一个工作流程的可执行表现形式的最佳途径。

Windows Workflow Foundation(以下简称WWF)提供了一个编程框架和工具以开发和执行各种不同的基于工作流的应用程序,比如文档管理、线型的商业应用、贸易单据流程、IT管理、B2B应用以及消费者应用。

有状态的、持久化的、不间断运行的应用程序

WWF简化了创造有状态的,不间断运行的异步工作流应用程序的过程。WWF运行时引擎管理工作流的运行,为工作流的长期运行提供保障,并能抵抗机器的重启。WWF运行时服务提供了一系列的附加功能,例如WWF服务为能温和且正确的处理错误提供了事务和持久化。

工作流模型

WWF为开发人员提供了一个工作流模型,来描述应用程序所需要的处理过程。通过使用工作流模型所提供的流程控件、状态管理、事务和同步器,开发人员可以分离应用程序逻辑和业务逻辑,构造一个高层次的抽象,达到提高开发者效率的目的。

组件的重用

WWF为开发者提供了一系列的活动——活动是一种包含了工作单元的可配置逻辑结构。这种结构封装了开发者可能经常性用到的一些部件,这样就节省了开发者的时间。

如果遇到一些特殊的需求或场景,WWF同样为开发自定义的活动提供了简单的方法。

通过将工作流引擎载入进程,WWF可以使任何应用程序和服务容器运行工作流。

运行时服务组件被设计成可插件形式的,这个可使应用程序以最合适的方式来提供它们的服务。WWF还提供了一组运行时服务的默认实现,这些服务能满足大部分类型的应用程序。

另外,WWF还提供了对ASP.NET的out-of-the-box(啥意思?)支持,让构造和运行能在IIS和ASP.NET环境的工作流变得简单。

Windows Workflow Foundation之旅(二)——指南1(构造一个顺序工作流)

翻译自 ms-help://MS.WinWF.v1.EN/WinWF_GettingStarted/html/9c3e5551-4eff-4977-89ac-f81ab092d996.htm

顺序工作流(sequential workflow)是为执行一种由一系列预定义的步骤组成的任务而设计的。这种体系结构是模拟基于过程的应用程序的。这一节将用几个步骤来编写一个简单的开支报告程序,这个小程序使用WinFrom做界面,用顺序工作流做业务逻辑。

这个小程序有一个TextBox来输入开支报告的总数,一个Button点击提交报告。工作流将评估开支,如果开支小于1000则提请领班审批,如果大于等于1000则提请经理审批。之后,工作流会发送一个审批意见,此时,出现一个Label显示审批意见,两个Button分别表示通过和拒绝审批。当某一个按钮被点击的时候,应用程序会通知回应工作流,工作流继续处理发生的事件。

开始构造顺序工作流

创建工作流类

WWF SDK中定义了一个SequentialWorkFlow类,我们定义一个ExpenseRoportWorkflow类,并继承自SequentialWorkflow,这样就创建一个顺序工作流。如:

声明工作流参数

 1using System;
 2using System.Workflow.Activities;
 3using System.Workflow.Activities.Rules;
 4
 5namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow
 6
{
 7    [RuleConditionsAttribute(typeof(Microsoft.Samples.
Workflow.Quickstarts.SequentialWorkflow.ExpenseReportWorkflow))]
 8    public sealed partial class ExpenseReportWorkflow :
 System.Workflow.Activities.SequentialWorkflow
 9    
{
10        public ExpenseReportWorkflow()
11        
{
12
13        }

14    }

15}

16

 

在一个工作流运行时,它可以从宿主应用程序中接收参数。参数是ParameterDeclaration类型的对象,一旦工作流初始化完成,参数的值就能通过工作流的Parameters集合来访问。

这里的开始报告程序用了两个参数。第一个参数是开支的总数;第二个是一个传出参数,用来放置审批意见。

定义一个新的方法InitializeComponent,并在构造ExpenseRoportWorkflow类的构造函数中调用它。一下的例子示范了怎样定义两个参数并把它们加到Parameters集合中。


 1public ExpenseReportWorkflow()
 2
 3
{
 4
 5    InitializeComponent();
 6
 7}

 8
 9 
10
11private void InitializeComponent()
12
13
{
14
15    System.Workflow.ComponentModel.ParameterDeclaration Amount = 
new System.Workflow.ComponentModel.ParameterDeclaration();
16
17    System.Workflow.ComponentModel.ParameterDeclaration Result = 
new System.Workflow.ComponentModel.ParameterDeclaration();
18
19    //

20
21    // Workflow Parameters
22
23    //
24

25    Amount.Direction = System.Workflow.ComponentModel.ParameterDirection.In;
26
27    Amount.Name = "Amount";
28
29    Amount.Type = typeof(int);
30
31    Amount.Value = null;
32
33    Result.Direction = System.Workflow.ComponentModel.ParameterDirection.Out;
34
35    Result.Name = "Result";
36
37    Result.Type = typeof(string);
38
39    Result.Value = null;
40
41    this.Parameters.Add(Amount);
42
43    this.Parameters.Add(Result);
44
45}

46
47

使用IfElse活动

IfElse活动用条件表达式来控制工作流中流程的运行。工作流将根据条件表达式的结果来决定执行条件分支(IfElseBranch)中的哪一个活动。

例子中将使用IfElse活动。通过判断从宿主应用程序中传入的Amount参数的值是否小于1000,来决定是否将审报发送到领班,否则发送到经理。

创建IfElse活动

1.定义4个私有变量

类型
名称
IfElse evaluateExpenseReportAmount
IfElseBranch ifNeedsLeadApproval
IfElseBranch elseNeedsManagerApproval
CodeCondition ifElseLogicStatement

2.在InitializeComponent中用默认构造函数实例以上4个对象。

以下的代码示例了怎样创建IfElse活动,并用IfElseBranch活动联系两个逻辑分支。你需要把以下代码放到InitializeComponent方法底部。

 1// 
 2
 3// EvaluateExpenseReportAmount
 4
 5// 

 6
 7this.EvaluateExpenseReportAmount.Activities.Add(this.ifNeedsLeadApproval);
 8
 9this.EvaluateExpenseReportAmount.Activities.Add(this.elseNeedsManagerApproval);
10
11this.EvaluateExpenseReportAmount.ID = "EvaluateExpenseReportAmount";
12
13// 
14
15// ifNeedsLeadApproval
16
17// 

18
19this.ifNeedsLeadApproval.Activities.Add(this.invokeGetLeadApproval);
20
21ifElseLogicStatement.Condition += new System.Workflow.Activities.
ConditionalExpression(this.DetermineApprovalContact);
22
23this.ifNeedsLeadApproval.Condition = ifElseLogicStatement;
24
25this.ifNeedsLeadApproval.ID = "ifNeedsLeadApproval";
26
27// 
28
29// elseNeedsManagerApproval
30
31// 

32
33this.elseNeedsManagerApproval.Activities.Add(this.invokeGetManagerApproval);
34
35this.elseNeedsManagerApproval.Condition = null;
36
37this.elseNeedsManagerApproval.ID = "elseNeedsManagerApproval";
38

WWF在IfElse活动中,有两种评估条件表达式的方式。一种是RoleCondition,这个对象通过使用一组规则来判断条件表达式的结果;另一种就是使用CodeCondition活动。CodeCondition使用一个回调方法,这个回调方法返回一个代表评估结果的布尔值。上面的例子就是使用CodeCondition来决定条件表达式的值。如果Amount参数小于1000,回调方法返回true,否则返回false。以下的代码就是这个回调函数的定义,你可以把它加到工作流类的定义中。

 1private bool DetermineApprovalContact(object sender, EventArgs e)
 2
 3
{
 4
 5    if ( Convert.ToInt32(this.Parameters["Amount"].Value) < 1000 )
 6
 7        return true;
 8
 9 
10
11    return false;
12
13}

14

 

构造IfElse分支(IfElseBranch)活动

创建完IfElse活动之后,我们来构造IfElseBranch活动

在这个例子中,每一IfElse活动的分支都使用InvokeMethodActivity活动来通知宿主程序——工作流需要领班或经理的审批才能继续执行。InvokeMethodActivity被设计成调用一个在WWF运行时中的服务接口。我们在同一份代码文件中定义了这个接口。当我们在之后构造宿主程序时,宿主类将实现这个接口,以便能建立工作流和宿主程序的通信(这一段文档上写的很模糊,我reflect后看了源码才明白过来,在最后将补充描述一下)。

构建IfElseBranch活动

1. 在类中定义两个私有字段

类型
名称
InvokeMethodActivity invokeGetLeadApproval
InvokeMethodActivity invokeGetManagerApproval

2. 在InitializeComponent中用默认构造函数实例化这两个对象

以下的代码示例了怎样在父活动(IfElse)中创建IfElseBranch活动,并把两个的InvokeMethodActivity联系到对应的IfElseBranch活动上,每个InvokeMethodActivity将调用定义在IExpenseReportService接口中的方法,接口会在稍微实现。你需要把以下代码放到InitializeComponent方法底部。

 

 1// 
 2
 3// invokeGetLeadApproval
 4
 5// 

 6
 7this.invokeGetLeadApproval.ID = "invokeGetLeadApproval";
 8
 9this.invokeGetLeadApproval.InterfaceType = typeof(Microsoft.
Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
10
11this.invokeGetLeadApproval.MethodName = "GetLeadApproval";   
12
13// 
14
15// invokeGetManagerApproval
16
17// 

18
19this.invokeGetManagerApproval.ID = "invokeGetManagerApproval";
20
21this.invokeGetManagerApproval.InterfaceType = typeof(Microsoft.
Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
22
23this.invokeGetManagerApproval.MethodName = "GetManagerApproval";
24
25

以下代码定义了IExpenseReportService接口

 

 1using System;
 2
 3using System.Workflow.ComponentModel;
 4
 5using System.Workflow.Runtime.Messaging;
 6
 7 
 8
 9namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow
10
11
{
12
13    [DataExchangeService]
14
15    public interface IExpenseReportService
16
17    
{
18
19        void GetLeadApproval();
20
21        void GetManagerApproval();
22
23        event EventHandler<WorkflowMessageEventArgs> ExpenseReportApproved;
24
25        event EventHandler<WorkflowMessageEventArgs> ExpenseReportRejected;
26
27    }

28
29}

30
31

 

监听宿主事件

在这个阶段,工作流已经从宿主程序接受了两个参数(译者注:其中一个为out参数,此时设为null),评估了Amount参数,作出了到底该提请谁确认审批的决定,并通知了宿主程序在继续接下来的处理之前,确认审批。这里,Listen活动和EventSinkActivity活动往往配合使用,来监听宿主程序触发指定的事件。接着,一个approval或rejection事件被引发,工作流继续执行,返回审批结果Result,并终止流程。

Listen活动的每个分支是一个EventDriven活动。EventDriven活动只能使用实现了IEventActivity接口的活动。Listen活动的每个分支中的EventDriven各有一个EventSinkActivity,它们是用来监听宿主程序触发的ExpenseReportApproved或者ExpenseReportRejected事件的。这种工作流和宿主的通信方法其实类似于之前的InvokeMethodActivity的过程,只不过前者是工作流监听宿主事件,而后者是宿主事件工作流中注册的接口。

我们已经在前面的步骤中,把接口和两个事件的定义都完成了。在这里,我们将创建一个Listen活动并和两个EventDriven分支建立连接。每个分支包含一个EventSinkActivity活动,每个EventSink监听一种对应的事件。此外,我们还将创建一些事件处理程序,来处理AfterInvoke事件(译者注:这里的AfterInvoke事件应为Invoked事件)。这些事件处理程序将会把Result参数的值设为approval或者rejected。

构造监听活动

1.在工作流类中定义5个私有字段

类型
名称
Listen
listenApproveReject
EventDriven approveEventDriven
EventDriven rejectEventDriven
EventSinkActivity approveEvent
EventSinkActivity rejectEvent

2. 在InitializeComponent中实例化。

以下的代码示例了怎样在创建Listen活动和EventSinkActivity活动,来监听宿主程序发出的事件。你需要把以下代码放到InitializeComponent方法底部。

 1// 
 2
 3// listenApproveReject
 4
 5// 

 6
 7this.listenApproveReject.Activities.Add(this.approveEventDriven);
 8
 9this.listenApproveReject.Activities.Add(this.rejectEventDriven);
10
11this.listenApproveReject.ID = "listenApproveReject";
12
13// 
14
15// approveEventDriven
16
17// 

18
19this.approveEventDriven.Activities.Add(this.approveEvent);
20
21this.approveEventDriven.ID = "approveEventDriven";
22
23// 
24
25// approveEvent
26
27// 

28
29this.approveEvent.EventName = "ExpenseReportApproved";
30
31this.approveEvent.ID = "approveEvent";
32
33this.approveEvent.InterfaceType = typeof(Microsoft.Samples.
Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
34
35this.approveEvent.Roles = null;
36
37this.approveEvent.Invoked += new System.EventHandler
(this.approveEvent_Invoked);
38
39// 
40
41// rejectEventDriven
42
43// 

44
45this.rejectEventDriven.Activities.Add(this.rejectEvent);
46
47this.rejectEventDriven.ID = "rejectEventDriven";
48
49// 
50
51// rejectEvent
52
53// 

54
55this.rejectEvent.EventName = "ExpenseReportRejected";
56
57this.rejectEvent.ID = "rejectEvent";
58
59this.rejectEvent.InterfaceType = typeof(Microsoft.Samples.
Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
60
61this.rejectEvent.Roles = null;
62
63this.rejectEvent.Invoked += new System.EventHandler
(this.rejectEvent_Invoked);
64
65

使用EventSinkActivity时,为了在工作流中加入一些附加逻辑,你可以为Invoked事件创建一个事件处理程序。一下是事件处理程序的代码。

 1private void approveEvent_Invoked(object sender, EventArgs e)
 2
 3
{
 4
 5    this.Parameters["Result"].Value = "Report Approved";
 6
 7}

 8
 9 
10
11private void rejectEvent_Invoked(object sender, EventArgs e)
12
13
{
14
15    this.Parameters["Result"].Value = "Report Rejected";
16
17}

18
19

完成顺序工作流

这个工作流包括两个主要的步骤:第一,监听宿主程序的递交审批事件,并把传入的值作为工作流参数;第二,监听通过或拒绝审批消息。一下的代码示例了怎样通过把之前创建好的活动加到工作流活动集中,来完成工作流的构造。

 1// 
 2
 3// ExpenseReportWorkflow
 4
 5// 

 6
 7this.Activities.Add(this.EvaluateExpenseReportAmount);
 8
 9this.Activities.Add(this.listenApproveReject);
10
11this.DynamicUpdateCondition = null;
12
13this.ID = "ExpenseReportWorkflow";
14
15

 

创建宿主程序

WWF需要一个宿主程序来运行工作流。当程序开始运行,WWF运行时引擎也随之启动。而之前构造好的工作流,则到用户点击了Submit按钮后才真正启动。

建立一个新的源文件,取名Program。以下的代码包含了完整的WinForm应用程序。IExpenseReportService接口GetLeadApproval和GetmanagerApproval方法已经定义在另一个文件中。宿主程序实现了这个接口。在approval和rejected按钮的click事件中,将引发ExpenseReprotApproval或ExpenseReprotRejected事件。

 

  1using System;
  2
  3using System.ComponentModel;
  4
  5using System.Drawing;
  6
  7using System.Windows.Forms;
  8
  9using System.Collections.Generic;
 10
 11using System.Workflow.Runtime;
 12
 13using System.Workflow.Runtime.Hosting;
 14
 15using System.Workflow.Runtime.Messaging;
 16
 17 
 18
 19namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflowHost
 20
 21
{
 22
 23    public class MainForm : Form, Microsoft.Samples.
Workflow.Quickstarts.SequentialWorkflow.
IExpenseReportService
 24
 25    
{
 26
 27        private System.Windows.Forms.Label label1;
 28
 29        private System.Windows.Forms.TextBox result;
 30
 31        private System.Windows.Forms.Label label2;
 32
 33        private System.Windows.Forms.Button submitButton;
 34
 35        private System.Windows.Forms.Label approvalState;
 36
 37        private System.Windows.Forms.Button approveButton;
 38
 39        private System.Windows.Forms.Button rejectButton;
 40
 41        private System.Windows.Forms.TextBox amount;
 42
 43        private System.Windows.Forms.Panel panel1;
 44
 45 
 46
 47        private System.ComponentModel.IContainer components = null;
 48
 49 
 50
 51        private delegate void GetApprovalDelegate();
 52
 53        private WorkflowRuntime workflowRuntime = null;
 54
 55        private WorkflowInstance workflowInstance = null;
 56
 57 
 58
 59        public MainForm()
 60
 61        
{
 62
 63            InitializeComponent();
 64
 65 
 66
 67            // Collapse approve/reject panel

 68
 69            this.Height -= this.panel1.Height;
 70
 71 
 72
 73            workflowRuntime = new WorkflowRuntime();
 74
 75            workflowRuntime.AddService(this);
 76
 77            workflowRuntime.StartRuntime();
 78
 79 
 80
 81            workflowRuntime.WorkflowCompleted += new EventHandler
<WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted);
 82
 83        }

 84
 85 
 86
 87        protected override void Dispose(bool disposing)
 88
 89        
{
 90
 91            if (disposing && (components != null))
 92
 93            
{
 94
 95                components.Dispose();
 96
 97            }

 98
 99            base.Dispose(disposing);
100
101        }

102
103 
104
105        private void InitializeComponent()
106
107        
{
108
109            this.label1 = new System.Windows.Forms.Label();
110
111            this.result = new System.Windows.Forms.TextBox();
112
113            this.label2 = new System.Windows.Forms.Label();
114
115            this.submitButton = new System.Windows.Forms.Button();
116
117            this.approvalState = new System.Windows.Forms.Label();
118
119            this.approveButton = new System.Windows.Forms.Button();
120
121            this.rejectButton = new System.Windows.Forms.Button();
122
123            this.amount = new System.Windows.Forms.TextBox();
124
125            this.panel1 = new System.Windows.Forms.Panel();
126
127            this.panel1.SuspendLayout();
128
129            this.SuspendLayout();
130
131            // 
132
133            // label1
134
135            // 

136
137            this.label1.AutoSize = true;
138
139            this.label1.Location = new System.Drawing.Point(13, 13);
140
141            this.label1.Name = "label1";
142
143            this.label1.Size = new System.Drawing.Size(39, 13);
144
145            this.label1.TabIndex = 1;
146
147            this.label1.Text = "Amount";
148
149            // 
150
151            // result
152
153            // 

154
155            this.result.Location = new System.Drawing.Point(13, 69);
156
157            this.result.Name = "result";
158
159            this.result.ReadOnly = true;
160
161            this.result.Size = new System.Drawing.Size(162, 20);
162
163            this.result.TabIndex = 1;
164
165            this.result.TabStop = false;
166
167            // 
168
169            // label2
170
171            // 

172
173            this.label2.AutoSize = true;
174
175            this.label2.Location = new System.Drawing.Point(13, 54);
176
177            this.label2.Name = "label2";
178
179            this.label2.Size = new System.Drawing.Size(33, 13);
180
181            this.label2.TabIndex = 3;
182
183            this.label2.Text = "Result";
184
185            // 
186
187            // submitButton
188
189            // 

190
191            this.submitButton.Enabled = false;
192
193            this.submitButton.Location = new System.Drawing.Point(56, 95);
194
195            this.submitButton.Name = "submitButton";
196
197            this.submitButton.Size = new System.Drawing.Size(75, 23);
198
199            this.submitButton.TabIndex = 2;
200
201            this.submitButton.Text = "Submit";
202
203            this.submitButton.Click += new System.EventHandler(this.submitButton_Click);
204
205            // 
206
207            // approvalState
208
209            // 

210
211            this.approvalState.AutoSize = true;
212
213            this.approvalState.Location = new System.Drawing.Point(10, 9);
214
215            this.approvalState.Name = "approvalState";
216
217            this.approvalState.Size = new System.Drawing.Size(45, 13);
218
219            this.approvalState.TabIndex = 4;
220
221            this.approvalState.Text = "Approval";
222
223            // 
224
225            // approveButton
226
227            // 

228
229            this.approveButton.Enabled = false;
230
231            this.approveButton.Location = new System.Drawing.Point(11, 25);
232
233            this.approveButton.Name = "approveButton";
234
235            this.approveButton.Size = new System.Drawing.Size(75, 23);
236
237            this.approveButton.TabIndex = 0;
238
239            this.approveButton.Text = "Approve";
240
241            this.approveButton.Click += new System.EventHandler(this.approveButton_Click);
242
243            // 
244
245            // rejectButton
246
247            // 

248
249            this.rejectButton.Enabled = false;
250
251            this.rejectButton.Location = new System.Drawing.Point(86, 25);
252
253            this.rejectButton.Name = "rejectButton";
254
255            this.rejectButton.Size = new System.Drawing.Size(75, 23);
256
257            this.rejectButton.TabIndex = 1;
258
259            this.rejectButton.Text = "Reject";
260
261            this.rejectButton.Click += new System.EventHandler(this.rejectButton_Click);
262
263            // 
264
265            // amount
266
267            // 

268
269            this.amount.Location = new System.Drawing.Point(13, 29);
270
271            this.amount.MaxLength = 9;
272
273            this.amount.Name = "amount";
274
275            this.amount.Size = new System.Drawing.Size(162, 20);
276
277            this.amount.TabIndex = 0;
278
279            this.amount.KeyPress += new System.Windows.Forms.
KeyPressEventHandler(this.amount_KeyPress);
280
281            this.amount.TextChanged += new System.EventHandler
(this.amount_TextChanged);
282
283            // 
284
285            // panel1
286
287            // 

288
289            this.panel1.Controls.Add(this.approvalState);
290
291            this.panel1.Controls.Add(this.approveButton);
292
293            this.panel1.Controls.Add(this.rejectButton);
294
295            this.panel1.Location = new System.Drawing.Point(3, 124);
296
297            this.panel1.Name = "panel1";
298
299            this.panel1.Size = new System.Drawing.Size(172, 66);
300
301            this.panel1.TabIndex = 8;
302
303            // 
304
305            // MainForm
306
307            // 

308
309            this.AcceptButton = this.submitButton;
310
311            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
312
313            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
314
315            this.ClientSize = new System.Drawing.Size(187, 201);
316
317            this.Controls.Add(this.panel1);
318
319            this.Controls.Add(this.amount);
320
321            this.Controls.Add(this.submitButton);
322
323            this.Controls.Add(this.label2);
324
325            this.Controls.Add(this.result);
326
327            this.Controls.Add(this.label1);
328
329            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
330
331            this.MaximizeBox = false;
332
333            this.MinimizeBox = false;
334
335            this.Name = "MainForm";
336
337            this.Text = "Simple Expense Report";
338
339            this.panel1.ResumeLayout(false);
340
341            this.panel1.PerformLayout();
342
343            this.ResumeLayout(false);
344
345            this.PerformLayout();
346
347 
348
349        }

350
351 
352
353        private void submitButton_Click(object sender, EventArgs e)
354
355        
{
356
357            Type type = typeof(Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.ExpenseReportWorkflow);
358
359 
360
361            // Construct workflow parameters

362
363            Dictionary<string, object> properties = new Dictionary<string, object>();
364
365            properties.Add("Amount", Int32.Parse(this.amount.Text));
366
367            properties.Add("Result", string.Empty);
368
369 
370
371            // Start the workflow

372
373            workflowInstance = workflowRuntime.StartWorkflow(type, properties);
374
375        }

376
377 
378
379        void workflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
380
381        
{
382
383            if (this.result.InvokeRequired)
384
385                this.result.Invoke(new EventHandler<WorkflowCompletedEventArgs>
(this.workflowRuntime_WorkflowCompleted), sender, e);
386
387            else

388
389            
{
390
391                this.result.Text = e.OutputParameters["Result"].ToString();
392
393 
394
395                // Clear fields

396
397                this.amount.Text = string.Empty;
398
399 
400
401                // Disable buttons

402
403                this.approveButton.Enabled = false;
404
405                this.rejectButton.Enabled = false;
406
407            }

408
409        }

410
411 
412
413        private void approveButton_Click(object sender, EventArgs e)
414
415        
{
416
417            // Raise the ExpenseReportApproved event back to the workflow

418
419            ExpenseReportApproved(null, new WorkflowMessageEventArgs
(this.workflowInstance.InstanceId));
420
421            this.Height -= this.panel1.Height;
422
423            this.submitButton.Enabled = true;
424
425        }

426
427 
428
429        private void rejectButton_Click(object sender, EventArgs e)
430
431        
{
432
433            // Raise the ExpenseReportRejected event back to the workflow

434
435            ExpenseReportRejected(null, new WorkflowMessageEventArgs
(this.workflowInstance.InstanceId));
436
437            this.Height -= this.panel1.Height;
438
439            this.submitButton.Enabled = true;
440
441        }

442
443 
444
445        IExpenseReportService Members
#region IExpenseReportService Members
446
447 
448
449        public void GetLeadApproval()
450
451        
{
452
453            if (this.approvalState.InvokeRequired)
454
455                this.approvalState.Invoke(new GetApprovalDelegate(this.GetLeadApproval));
456
457            else

458
459            
{
460
461                this.approvalState.Text = "Lead approval needed";
462
463                this.approveButton.Enabled = true;
464
465                this.rejectButton.Enabled = true;
466
467 
468
469                // expand the panel

470
471                this.Height += this.panel1.Height;
472
473                this.submitButton.Enabled = false;
474
475            }

476
477        }

478
479 
480
481        public void GetManagerApproval()
482
483        
{
484
485            if (this.approvalState.InvokeRequired)
486
487                this.approvalState.Invoke(new GetApprovalDelegate(this.GetManagerApproval));
488
489            else

490
491            
{
492
493                this.approvalState.Text = "Manager approval needed";
494
495                this.approveButton.Enabled = true;
496
497                this.rejectButton.Enabled = true;
498
499 
500
501                // expand the panel

502
503                this.Height += this.panel1.Height;
504
505                this.submitButton.Enabled = false;
506
507            }

508
509        }

510
511 
512
513        public event EventHandler<WorkflowMessageEventArgs> ExpenseReportApproved;
514
515        public event EventHandler<WorkflowMessageEventArgs> ExpenseReportRejected;
516
517 
518
519        #endregion

520
521 
522
523        private void amount_KeyPress(object sender, KeyPressEventArgs e)
524
525        
{
526
527            if (!Char.IsControl(e.KeyChar) && (!Char.IsDigit(e.KeyChar)))
528
529                e.KeyChar = Char.MinValue;            
530
531        }

532
533 
534
535        private void amount_TextChanged(object sender, EventArgs e)
536
537        
{
538
539            submitButton.Enabled = amount.Text.Length > 0;
540
541        }

542
543    }

544
545 
546
547    static class Program
548
549    
{
550
551        /**/
/// <summary>
552
553        /// The main entry point for the application.
554
555        /// </summary>

556
557        [STAThread]
558
559        static void Main()
560
561        
{
562
563            Application.EnableVisualStyles();
564
565            Application.Run(new MainForm());
566
567        }

568
569    }

570
571}

572
573

 

Ps:上面还有个问题没有解释清楚,我reflect了一下,看了源码才知道个大概。
那就是IfElseBranch中的InvokeMethodActivity。

InvokeMethodActivity中有一个Type类型的InterfaceType属性,使用时,需要设置这个属性,并把MethodName设为这个接口中的一个方法的名称。运行时,工作流引擎(也就是WorkflowRuntime)将通过反射调用这个接口。

但引擎怎么知道调用接口的哪个实现呢?你看

workflowRuntime = new WorkflowRuntime();

workflowRuntime.AddService(this);

workflowRuntime.StartRuntime();

原来,初始化引擎时,我们已经把实现了这个interface的类型的实例(this)注册到工作流中了。运行时,引擎就遍历所有已经注册的服务,如果实现了这个接口,这调用它。

另外,可以注册一个以上的服务,工作流引擎将调用所有实现了相应接口的接口方法。

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号