WWF将工作流分为两大类:
- 面向Human:在工作流运行时通过用户对外部应用程序的操作来影响工作流的业务流转。
- 面向System:应用程序控制流程。
工作流与应用程序都是可以单独存在的,因此它们之间的数据交互需要通过接口来实现。工作流、应用程序、接口程序之间的数据交互模型如下图:
工作流的实例存在于一个工作流运行时的Runtime容器中,但是用于数据交互的接口项目和应用程序都不存在于该工作流的Runtime容器中。WWF提供了"System.Workflow.Activities.ExternalDataExchangeService"专门用于它们的数据交互。用户可以把外部应用程序加载到"ExternalDataExchangeService"服务中,然后再将该服务加载到工作流运行时的Runtime容器中,那么这时工作流的实例和外部应用程序都处于同一个容器里,这样就可以进行数据交互了。
一、HandleExternalEvent(外部程序调用工作流)
HandleExternalEvent是一个事件类型的活动。CallExternalEvent和HandleExternalEvent活动与应用程序之间的相互调用关系如下:
HandleExternalEvent是通过一个"ExternalDataExchangeService"服务将应用程序注册到工作流运行时容器Runtime中,并通过一个继承了"ExternalDataExchange"的接口项目来实现外部应用程序对工作流的调用。但是HandleExternalEvent活动在接口中定义的是Event事件而不是普通的函数或方法。当工作流执行到HandleExternalEvent活动后会进入到"idle"状态。
以下示例主要演示外部调用工作流引擎。
示例:创建一个控制台程序,一个顺序工作流,一个Winform程序。
控制台程序仅仅一个接口:
namespace ClassLibrary1 { [ExternalDataExchange] //using System.Workflow.Activities; public interface IEvent { event EventHandler<ExternalDataEventArgs> MyEvent1; } }
InterfaceType属性将活动HandleExternalEvent与接口进行绑定。
Invoked添加一个事件,弹出对话框提示工作流是否已被调用。
工作流代码:
public sealed partial class Workflow1 : SequentialWorkflowActivity { public Workflow1() { InitializeComponent(); } private void Call(object sender, ExternalDataEventArgs e) { MessageBox.Show("工作流已被调用!"); } }
窗口程序代码:
public partial class Form1 : Form,ClassLibrary1.IEvent { private WorkflowRuntime wfRuntime = null; //using System.Workflow.Runtime; private WorkflowInstance wfInstance = null; private ExternalDataExchangeService externalService = null; //using System.Workflow.Activities; public event EventHandler<ExternalDataEventArgs> MyEvent1; public Form1() { InitializeComponent(); wfRuntime = new WorkflowRuntime(); externalService = new ExternalDataExchangeService(); wfRuntime.AddService(externalService); externalService.AddService(this); wfRuntime.StartRuntime(); } private void button1_Click(object sender, EventArgs e) { wfInstance = wfRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1)); wfInstance.Start(); } private void button2_Click(object sender, EventArgs e) { ExternalDataEventArgs guid = new ExternalDataEventArgs(wfInstance.InstanceId); MyEvent1(null, guid); } }
显示:
二、CallExternalMethod(工作流调用外部程序)
在实际项目中除了应用程序调用工作流外,工作流也可能需要调用应用程序,以便把工作流运行结果反馈给操作用户。WWF提供了CallExternalMethod活动来实现这个功能。
注意:HandleExternalEvent范例中接口定义的内容不同,外部应用程序是通过接口定义的“事件”来调用工作流项目,而工作流项目则是通过接口中定义的“方法”来调用应用程序的。
首先添加一个接口代码如下:
[ExternalDataExchange] //using System.Workflow.Activities; public interface ICallExtennalMethod { void MyCallExternalMethod(); }
工作流如下:
设置属性如下:
窗体代码如下:
public partial class Form1 : Form, ICallExtennalMethod { private WorkflowRuntime wfRuntime = null; //using System.Workflow.Runtime; private WorkflowInstance wfInstance = null; private ExternalDataExchangeService externalService = null; //using System.Workflow.Activities; public Form1() { InitializeComponent(); wfRuntime = new WorkflowRuntime(); externalService = new ExternalDataExchangeService(); wfRuntime.AddService(externalService); externalService.AddService(this); wfRuntime.StartRuntime(); } private void button1_Click(object sender, EventArgs e) { //创建一个CallExternalMethod_Workflow工作流实例 wfInstance = wfRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1)); //启动工作流实例 wfInstance.Start(); } private void button2_Click(object sender, EventArgs e) { ExternalDataEventArgs guid = new ExternalDataEventArgs(wfInstance.InstanceId); } void ICallExtennalMethod.MyCallExternalMethod() { MessageBox.Show("工作流通过接口调用应用程序中的方法成功!"); } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { wfRuntime.StopRuntime(); } }
运行如下:
三、Listen(排他事件分支)
- Listen是一个容器类型活动;
- 专门用于存放事件类型活动;
- Listen可以由多条分支,每条分支都是由EventDriven容器类型活动构成,但它不能通过逻辑判断条件来确定哪条分支将被执行;
- Listen活动在工作流运行时处于事件的监听状态,哪条分支上的事件被触发,哪条分支就将被执行,而其他分支将不再被执行,即只有一条分支可以被执行,分支与分支之间是具有“排他”性的;
- Listen活动每条分支上的第一个子活动一定为事件类型的活动;
Listen活动默认情况下有两条分支,在很多实际的项目中Listen活动的其中一条分支往往使用Delay活动作为默认分支,在工作流运行时如果事件始终未触发,那么用户可以通过Delay活动设置一个超时的时间,当Listen活动超时后工作流可以继续执行其他后续工作。
Listen活动的每条分支都是由EventDriven活动构成的,因此在EventDriven容器中的第一个活动必须是事件类型活动,否则系统会给出错误提示。
示例:
首先定义一个接口如下:
[ExternalDataExchange] //using System.Workflow.Activities; public interface IListen { event EventHandler<ExternalDataEventArgs> ListenEvent1; event EventHandler<ExternalDataEventArgs> ListenEvent2; }
工作流如下:
设置属性如下:
代码如下:
public sealed partial class Workflow1 : SequentialWorkflowActivity { public Workflow1() { InitializeComponent(); } private void EventInvoked1(object sender, ExternalDataEventArgs e) { MessageBox.Show("执行HandleEvent1!"); } private void EventInvoked2(object sender, ExternalDataEventArgs e) { MessageBox.Show("执行HandleEvent2!"); } private void Code1(object sender, EventArgs e) { MessageBox.Show("工作流即将结束!"); } }
设计窗口程序如下:
代码如下:
public partial class Form1 : Form, ClassLibrary1.IListen { private WorkflowRuntime wfRuntime = null; //using System.Workflow.Runtime; private WorkflowInstance wfInstance = null; private ExternalDataExchangeService externalService = null; //using System.Workflow.Activities; public event EventHandler<ExternalDataEventArgs> ListenEvent1; public event EventHandler<ExternalDataEventArgs> ListenEvent2; public Form1() { InitializeComponent(); //创建工作流的运行时容器 wfRuntime = new WorkflowRuntime(); externalService = new ExternalDataExchangeService(); //加载外部数据交互服务 wfRuntime.AddService(externalService); externalService.AddService(this); wfRuntime.StartRuntime(); } private void button1_Click(object sender, EventArgs e) { //创建一个CallExternalMethod_Workflow工作流实例 wfInstance = wfRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1)); //启动工作流实例 wfInstance.Start(); btnEvent1.Enabled = true; btnEvent2.Enabled = true; } private void btnEvent1_Click(object sender, EventArgs e) { ExternalDataEventArgs args = new ExternalDataEventArgs(wfInstance.InstanceId); ListenEvent1(null,args); btnEvent1.Enabled = false; btnEvent2.Enabled = false; } private void btnEvent2_Click(object sender, EventArgs e) { ExternalDataEventArgs args = new ExternalDataEventArgs(wfInstance.InstanceId); ListenEvent2(null, args); btnEvent1.Enabled = false; btnEvent2.Enabled = false; } }
点击按钮显示如下:
四、Delay(定时超时)
Delay活动是一个事件类型的活动,它与HandleExternalEvent活动有所不同。Delay是由自身触发事件的,HandleExternalEvent活动是依赖外部来触发事件的。Delay活动常作为HandleExternalEvent等时间类型活动的补充,当一段时间内工作流没有被外部事件触发时,就认为工作流出现超时,那么继续执行后续的活动。该活动可以触发工作流的"WorkflowIdled"事件,使工作流进入"idle"状态。
示例:
定义一个接口如下:
[ExternalDataExchange] //using System.Workflow.Activities; public interface IListen { event EventHandler<ExternalDataEventArgs> ListenEvent1; }
设计工作流如下:
代码如下:
public sealed partial class Workflow1 : SequentialWorkflowActivity { public Workflow1() { InitializeComponent(); } private void EventInvoked1(object sender, ExternalDataEventArgs e) { MessageBox.Show("执行HandleEvent1!"); } private void Code1(object sender, EventArgs e) { MessageBox.Show("工作流即将结束!"); } }
启动Winform程序,当5秒内不点击,则自动弹出工作流即将结束对话框。
五、EventHandlingScope(无排他分支)
EventHandlingScope活动是一个容器类型的活动,它与Listen类似,每个分支都是由EventDriven容器类型的活动构成。 它与Listen活动的最大区别是EventHandlingScope活动有且只有一个主活动,而Listen活动却没有主活动的概念。 Listen活动每条分支之间都是平行关系,只要有一个分支监听到事件,其他分支就都不会被执行。而EventHandlingScope活动是在其活动运行结束前,它的所有事件的分支都可以被执行,不会出现“排他”的现象,该活动所监听的事件都存放在"Event Handlers"视图里。下面通过一个具体范例来对EventHandlingScope活动进行讲解。
下面以一个20秒内的投票系统作为示例:
首先定义一个接口如下:
[ExternalDataExchange] //using System.Workflow.Activities; public interface IEvent { event EventHandler<ExternalDataEventArgs> Approved; event EventHandler<ExternalDataEventArgs> Rejected; }
设计工作流如下:
顺序工作流:=》Code活动=》EventHandlingScope(里面放一个Delay,时间设为20秒),然后又是一个Code活动。
然后,在EventHandlingScope切换到事件视图:
然后分别放入两个EventDriven,再在EventDriven里面分别放入两个HandleExternalEvent。
最里面的HandleExternalEvent属性设置如下:
工作流代码如下:
public sealed partial class Workflow1 : SequentialWorkflowActivity { private string approvednum = "0"; private string rejectednum = "0"; public Workflow1() { InitializeComponent(); } private void ApproveEvent(object sender, ExternalDataEventArgs e) { ClassLibrary1.Voter vo = (ClassLibrary1.Voter)e; approvednum = vo.VoterNum; } private void RejectedEvent(object sender, ExternalDataEventArgs e) { ClassLibrary1.Voter vo = (ClassLibrary1.Voter)e; rejectednum = vo.VoterNum; } private void StartExecute(object sender, EventArgs e) { MessageBox.Show("工作流已启动!"); } private void TotalExecute(object sender, EventArgs e) { if (approvednum != "0" && rejectednum != "0") { MessageBox.Show("20秒内投赞成票人数:" + approvednum + ",投反对票人数:" + rejectednum); } else { MessageBox.Show("20秒内没有人投票!"); } } }
窗体程序设计如下:
窗体程序代码如下:
public partial class Form1 : Form, ClassLibrary1.IEvent { private WorkflowRuntime wfRuntime = null; //using System.Workflow.Runtime; private WorkflowInstance wfInstance = null; private ExternalDataExchangeService externalService = null; //using System.Workflow.Activities; public event EventHandler<ExternalDataEventArgs> Approved; public event EventHandler<ExternalDataEventArgs> Rejected; public Form1() { InitializeComponent(); //创建工作流的运行时容器 wfRuntime = new WorkflowRuntime(); externalService = new ExternalDataExchangeService(); //加载外部数据交互服务 wfRuntime.AddService(externalService); externalService.AddService(this); wfRuntime.StartRuntime(); } private void button1_Click(object sender, EventArgs e) { //创建一个CallExternalMethod_Workflow工作流实例 wfInstance = wfRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1)); //启动工作流实例 wfInstance.Start(); } private void btnApprove_Click(object sender, EventArgs e) { Approved(null, new ClassLibrary1.Voter(wfInstance.InstanceId, this.numericUpDown1.Value.ToString())); } private void btnRejected_Click(object sender, EventArgs e) { Rejected(null, new ClassLibrary1.Voter(wfInstance.InstanceId, this.numericUpDown1.Value.ToString())); } }
输出如下:
当20秒内投了票。
当20秒内没人投票时:
六、Parallel(平行顺序)
Parallel活动是一个容器类型的活动,该活动的每条分支都是由顺序活动构成的。在Parallel活动运行时,只有每条分支全部执行完毕后该活动才结束,工作流也才能执行Parallel活动的其他后续活动,否则工作流就将一直等待直到Parallel活动结束。
平行前面已经在例子中说明过。Parallel活动被广泛运用于工作流中的“会签”流程。
首先定义一个接口如下:
[ExternalDataExchange] //using System.Workflow.Activities; public interface IEvent { event EventHandler<ExternalDataEventArgs> CEOEvent; event EventHandler<ExternalDataEventArgs> CTOEvent; event EventHandler<ExternalDataEventArgs> CFOEvent; }
添加一个工作流如下:
注意,默认Parallel为两个分支,可以右键"添加分支"增加一个。
属性设置:
工作流代码如下:
public sealed partial class Workflow1 : SequentialWorkflowActivity { public Workflow1() { InitializeComponent(); } private void Code1(object sender, EventArgs e) { MessageBox.Show("会签结束!"); } private void CEOInvoked(object sender, ExternalDataEventArgs e) { MessageBox.Show("CEO签字!"); } private void CTOInvoked(object sender, ExternalDataEventArgs e) { MessageBox.Show("CTO签字!"); } private void CFOInvoked(object sender, ExternalDataEventArgs e) { MessageBox.Show("CFO签字!"); } }
增加一个控制台程序如下:
以上代码实现的效果是,当三个按钮都点击,即3个XXO都签字之后,就会弹出会签结束,XXO之间的签字顺序无影响,否则一直不会弹出结束。