Windows Workflow Foundation之旅(二)——指南1(创建顺序工作流)

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集合中。

 

使用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

 

 WWFIfElse活动中,有两种评估条件表达式的方式。一种是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活动往往配合使用,来监听宿主程序触发指定的事件。接着,一个approvalrejection事件被引发,工作流继续执行,返回审批结果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接口GetLeadApprovalGetmanagerApproval方法已经定义在另一个文件中。宿主程序实现了这个接口。在approvalrejected按钮的click事件中,将引发ExpenseReprotApprovalExpenseReprotRejected事件。

  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(1313
);
140

141            this.label1.Name = "label1"
;
142

143            this.label1.Size = new System.Drawing.Size(3913
);
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(1369);
156

157            this.result.Name = "result"
;
158

159            this.result.ReadOnly = true
;
160

161            this.result.Size = new System.Drawing.Size(16220
);
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(1354
);
176

177            this.label2.Name = "label2"
;
178

179            this.label2.Size = new System.Drawing.Size(3313
);
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(5695
);
194

195            this.submitButton.Name = "submitButton"
;
196

197            this.submitButton.Size = new System.Drawing.Size(7523
);
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(109
);
214

215            this.approvalState.Name = "approvalState"
;
216

217            this.approvalState.Size = new System.Drawing.Size(4513
);
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(1125
);
232

233            this.approveButton.Name = "approveButton"
;
234

235            this.approveButton.Size = new System.Drawing.Size(7523
);
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(8625
);
252

253            this.rejectButton.Name = "rejectButton"
;
254

255            this.rejectButton.Size = new System.Drawing.Size(7523
);
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(1329);
270

271            this.amount.MaxLength = 9
;
272

273            this.amount.Name = "amount"
;
274

275            this.amount.Size = new System.Drawing.Size(16220
);
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(3124
);
296

297            this.panel1.Name = "panel1"
;
298

299            this.panel1.Size = new System.Drawing.Size(17266
);
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(187201
);
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<stringobject> properties = new Dictionary<stringobject>();
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(nullnew 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(nullnew 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
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)注册到工作流中了。运行时,引擎就遍历所有已经注册的服务,如果实现了这个接口,这调用它。

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

你可能感兴趣的:(workflow)