关于动态更新
在开发工作流时,我们会在工作流设计器中通过拖拽活动,设置属性等等来设计我们的工作流,我们希望我们在设计时定义好的工作流在运行时是不变的。但在实际中我们往往需要在运行时来改变工作流的架构,例如我们已经定义好的流程在没有结束时我们需要增加一个流程点,这个时候我们就需要使用动态更新来现实。在WF中我们也可以对正在运行的工作流实例添加删除活动,修改活动,规则条件等。当动态更新应用到工作流实例上时,只影响当前的工作流实例。其他的和将来的都不受影响,仍然使用原始的工作流定义。
动态更新不适合要进行整体更改的情况,因为这将导致工作流与原始设计原则产生极大差别。 在此类情况下,应设计一个新的工作流,而不是对正在运行的实例进行更改。动态更新不必重新编译和重新启动工作流。
何时进行动态更新
我们可以从工作流的执行线程内部和外部对正在运行的工作流实例进行动态更新。 在内部,我们可以使用CodeActivity,自定义活动等来实现,由于工作流运行在单一的线程上,所以我们执行动态更新的时候不会有其他的活动在执行。 在外部,我们实现动态更新就有严格规定,主要有以下状态可以在宿主程序中进行动态更新:
1. 工作流实例创建完成,但是没有开始。
2. 工作流实例是挂起状态,没有恢复。
3. 工作流实例是空闲状态。
我们可以在WorkflowRuntime的WorkflowCreated,WorkflowSuspened,WorkflowIdled事件中来完成动作更新。WorkflowCreated事件在workflowRuntime.CreateWorkflow方法调用后引发,使用使用SuspendActivity可以使工作流转到挂起状态,引发WorkflowSuspened事件。使用DelayActivity或HandleExternalEventActivity时工作流会进入Idle状态,引发WorkflowIdled事件。
动态更新的一般步骤
我们通过以下几个步骤进行动态更新:
1.所有对运行中工作流实例的建议的更改必须使用WorkflowChanges对象进行。首先我们要创建一个该类的实例,构造函数中需要需要传递被更改的工作流实例的根活动。如果你是使用内部实现动态更新的方式,你直接传递this关键字就可以了。如果你是在外部宿主程序中,你需要使用workflowinstance的GetWorkflowDefinition方法来获取工作流实例的根活动。
2.WorkflowChanges对象被创建后。该对象的TransientWorkflow属性返回该工作流实例的克隆版本,我们会对该克隆版本进行更改,然后将更改应用到运行中的工作流实例。
3.我们需要找到你要添加或删除活动的父活动,可以使用CompositeActivity的Activities属性或是使用GetActivityByName方法来确定父活动,然后使用Add或Insert方法来添加或插入新的活动。移除活动使用remove方法。
4.使用WorkflowChanges的Validate方法来验证,如果有错误会返回ValidationError对象集合。
5.验证通过后就可以更新了,如果是在外部实现动作更新需要调用workflowinstance对象的ApplyWrokflowChange方法,内部的话就调用当前对象的ApplyworkflowChanges方法。
阻止动态更新
顺序和状态机工作流都有一个DynamicUpdateCondition属性,如果你没有设置该属性工作流一直允许动态更新,当你设置了该属性后,当你在调用ApplyWorkflowChanges方法时就会去计算,如果为true则允许进行动态更新,否则就会抛出异常。
动态更新实例
下面我们分别使用在工作流内部和外部两种方式来举例说明,首先我们建立一个顺序型工作流控制台程序,工作流设计如下图:
工作流代码如下:
public sealed partial class CaryDynamicUpdateWorkflow: SequentialWorkflowActivity { public CaryDynamicUpdateWorkflow() { InitializeComponent(); } private void beforeSequence_ExecuteCode(object sender, EventArgs e) { Console.WriteLine("beforeSequence执行了"); } private void afterSequence_ExecuteCode(object sender, EventArgs e) { Console.WriteLine("afterSequence执行了"); } private void IsUpdate(object sender, ConditionalEventArgs e) { e.Result = true; } }
我们会在运行时给该工作流中的sequenceActivityContainer添加一个自定义活动PrintActivity,该自定义活动仅仅向控制台输出一条语句,代码如下:
public partial class PrintActivity : System.Workflow.ComponentModel.Activity { public PrintActivity() { InitializeComponent(); } protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { Console.WriteLine("自定义Print活动"); return base.Execute(executionContext); } }
1.宿主程序中,首先我们在workflowRuntime的WorkflowCreated事件中执行动态更新.代码如下:
static void workflowRuntime_WorkflowCreated(object sender, WorkflowEventArgs e) { WorkflowChanges wc = new WorkflowChanges(e.WorkflowInstance.GetWorkflowDefinition()); CompositeActivity sequenceContainer = wc.TransientWorkflow.GetActivityByName ("sequenceActivityContainer") as CompositeActivity; if (sequenceContainer != null) { PrintActivity print = new PrintActivity(); sequenceContainer.Activities.Add(print); ValidationErrorCollection errors = wc.Validate(); if (errors.Count == 0) { try { e.WorkflowInstance.ApplyWorkflowChanges(wc); } catch (Exception ex) { Console.WriteLine("引用动态更新异常:{0}", ex.Message); } } else { foreach (ValidationError error in errors) { Console.WriteLine("验证错误:" + error.ToString()); } }
} }
3.从上图可以看出我们自定义的PrintActivity活动在运行时添加到了工作流实例当中,以上我们是在宿主程序中执行动态更新的,我们也可以在工作流内部来完成,例如我们将动态更新的代码放到我们重写的Initialize方法中,我们只要将如下代码做更改即可:
WorkflowChanges wc = new WorkflowChanges(e.WorkflowInstance.GetWorkflowDefinition());
try { e.WorkflowInstance.ApplyWorkflowChanges(wc); }
改为:
WorkflowChanges wc = new WorkflowChanges(this);
try { this.ApplyWorkflowChanges(wc); }
运行程序你可以得到和上面一样的结果。
4.如果你将工作流的DynamicUpdateCondition属性设为false,此时做动态更新时会抛出异常,如下图: