动态修改工作流
在实际工作中有时会需要对已经设计好的工作流进行修改,例如:某工作流中有一个部门经理审批的操作,但经理出差在外,而工作流设计又没有预料这种情况的放声,这时就可以通过动态修改的技术在不改变原有工作流设计的情况下,在宿主程序中通过代码对工作流进行修改,已达到与直接在工作流设计界面上修改相同的效果。
1.1 动态添加或移除活动
在WF工作流中,不管是顺序类型的还是状态机类型的工作流,都是容器类型的复合活动和非容器类型的活动所构成的。
1.1.1
可以向顺序工作流中动态添加一个活动,示例首先创建一个顺序类型的工作流项目,如图1所示,不添加任何活动到该工作流中。
图1 DynamicAddActivity示例工作流界面
然后创建一个Windows应用程序,在界面上添加一个按钮并将其命名为“动态添加一个Code活动”,示例将在按钮中实现动态加载活动的功能。
首先创建工作流的实例,并且通过“GetWorkflowDefinition”方法得到该示例中所有活动的集合,其代码如下所示。
Type type = typeof(Workflow1);
wfInstance = wfRuntime.CreateWorkflow(type);
Activity act = wfInstance.GetWorkflowDefinition();
示例将在这个空的工作流中动态添加一个Code类型的活动,那么在该方法要先构造一个Code活动并将它命名为“codeActivity”,并将它的属性”ExecuteCode”通过delegate方式绑定一个事件,代码如下所示。
CodeActivity code = new CodeActivity("Codeactivity1");
code.ExecuteCode += delegate(object s, EventArgs ea)
{
MessageBox.Show("codeactivity插入到工作流中");
};
最后通过“WorkflowChanges”复制一份盖工作流的对象集合,并且将构建的Code活动作为该顺序工作流中的第一个子活动进行插入,再将动态插入后的工作流对象集合应用回该工作流示例,最终实现动态添加活动的功能,代码如下所示。
WorkflowChanges wfchange = new WorkflowChanges(act);
wfchange.TransientWorkflow.Activities.Insert(0, code);
wfInstance.ApplyWorkflowChanges(wfchange);
wfInstance.Start();
运行示例,点击界面上的“动态添加一个Code活动”按钮,如图2所示,该Code活动被成功插入到工作流。
图2 DynamicAddActivity示例运行结果
1.1.2 动态的移除一个活动
可以向顺序工作流中动态移除一个活动,示例首先创建一个顺序类型的工作流项目,如图3,添加两个Code类型的活动,分别代表工作流启动和工作流结束。
图3
然后在Windows应用程序的界面上添加一个按钮并将它命名为“动态移除一个Code活动”。在该活动的“Click”事件中必须先创建工作流实例,并且通过“GetWorkDefinition”方法得到该实例中所有活动的集合,然后再通过“WorkflowChanges”复制一份盖工作流对象的集合到变量“wfchange”中,其代码如下。
Type type = typeof(Workflow1);
wfInstance = wfRuntime.CreateWorkflow(type);
Activity Act = wfInstance.GetWorkflowDefinition();
WorkflowChanges wfchange = new WorkflowChanges(Act);
用户可以在复制的工作流对象集合中找到要移除的活动,示例移除“codeActivity1”活动,在运行时不提示用户工作流已经启动,最后要将修改后的工作流对象集合重新应用回工作流的实例,代码如下:
Activity code1 = wfchange.TransientWorkflow.Activities["codeActivity1"];
wfchange.TransientWorkflow.Activities.Remove(code1);
wfInstance.ApplyWorkflowChanges(wfchange);
wfInstance.Start();
示例2
1.1.3 向容器中动态添加一个活动
可以向工作流的容器类型复合活动中添加一个子活动,示例首先添加一个顺序类型的工作流,如图4添加一个Sequence容器,然后将通过代码向其内部动态添加一个Code类型的子活动。
图4 工作流界面
在Windows应用程序的界面上添加一个按钮并将它命名为“在容器中动态添加一个Code活动”,在该按钮“Click”事件中向Sequence内部动态加载一个Code子活动。首先创建工作流实例,并且通过“GetWorkflowDefinition”方法得到该实例中所有活动的集合,然后通过一个Code类型的活动并将它命名为“codeactivity1”,然后将它的属性“ExecuteCode”通过delegate代理绑定一个可用于执行的事件。
Type type = typeof(Workflow1);
wfInstance = wfRuntime.CreateWorkflow(type);
Activity Act = wfInstance.GetWorkflowDefinition();
WorkflowChanges wfchange = new WorkflowChanges(Act);
CodeActivity code1 = new CodeActivity("codeactivity1");
code1.ExecuteCode += delegate(object s, EventArgs ea)
{
MessageBox.Show("codeactivity1被插入到工作流的Sequence活动中");
};
通过活动的唯一名称从工作流对象的集合中获取到Sequence活动,并将动态构建的“CodeActivity1”活动作为其子活动进行添加,再将修改后的工作流对象集合更新回工作流实例,代码如下。
SequenceActivity seq = (SequenceActivity)wfchange.TransientWorkflow.Activities["sequenceActivity1"];
seq.Activities.Add(code1);
wfInstance.ApplyWorkflowChanges(wfchange);
wfInstance.Start();
示例3
1.1.4 从容器中动态移除一个活动
可以从工作流的容器中类型活动中动态移除任何一个活动,示例首先创建一个顺序类型的工作流项目,如图5,添加两个Code类型的活动,分别代表工作流启动和工作流结束。
图5
在Windows应用程序的界面上添加一个按钮并将它命名为“在容器中动态移除一个Code活动后”,在该按钮的“Click”事件中从Sequence内部动态移除一个Code子活动。首先创建工作流的实例,通过“GetWorkflowDefinition”方法得到该实例中所有活动的集合,然后通过“WorkflowChanges”复制一份盖工作流对象的集合到变量“wfchange”中,接下俩将通过活动的唯一名称“SequenceActivity1”从工作流的对象集合中获取到对应Sequence活动,并按照子活动在容器内的索引编号将其进行移除,最后将修改后的工作流对象集合更新回工作流的实例,代码如下所示。
Type type = typeof(Workflow1);
wfInstance = wfRuntime.CreateWorkflow(type);
Activity Act = wfInstance.GetWorkflowDefinition();
WorkflowChanges wfchange = new WorkflowChanges(Act);
SequenceActivity seq=(SequenceActivity)wfchange.TransientWorkflow.Activities["sequenceActivity1"];
seq.Activities.RemoveAt(0);
wfInstance.ApplyWorkflowChanges(wfchange);
wfInstance.Start();
1.2 动态修改判断条件
在WF提供的各种活动控件中,向IfElse和While这两种基于规则模式的活动时需
要设置其逻辑判断条件的。该判断条件份为两种类型,一种是“Code Condition”,利用程序代码来定义其逻辑的判断条件,另外一种是“Declarative Rule Condition”,利用WF提供的判断条件编辑器来定义逻辑的判断条件。
1.2.1 创建工作流项目
创建一个顺序类型的工作流项目,然后添加一个IfElse活动到该工作流设计界面。将IfElse活动控件的两条分支分别命名为“IfBranch”和“ElseBranch”,最后再为每条分支各添加一个Code活动控件,完成后的工作流界面如图1所示。
图1
接下来以“Declarative Rule Condition”方式利用WF提供的判断条件编辑器来定义“IfBrach”分支的逻辑判断条件。如图2所示,所定义判断条件名称为“Condition1”,其含义是:当工作流中定义的参数“Condition”小于“0”时,该表达式为“真”并执行“IfBrach”分支。
图2
在示例中将通过应用程序来动态修改工作流定义的变量“Condition”的值,以及IfElse的判断条件,从而使IfElse活动控件在运行时可以执行“ElseBrach”分支。示例工作流“Workflow1.cs”文件完整代码如下所示。
public sealed partial class Workflow1: SequentialWorkflowActivity
{
private int condition;
public int Condition
{
get { return condition; }
set { condition = value; }
}
public Workflow1()
{
InitializeComponent();
}
private void Code1(object sender, EventArgs e)
{
MessageBox.Show("If分支被执行");
}
private void Code2(object sender, EventArgs e)
{
MessageBox.Show("Else分支被执行");
}
}
1.2.2 创建应用程序项目
根据上面工作流的定义,示例将允许用户输入参数“Condition”的值,以及与它进行比较判断条件的值,应用程序会将其传入工作流并执行“Condition1”表达式,从而影响工作流的运行结果,应用程序界面如图3所示。
图3
WF对所有运行中的工作流都可以通过“WorkflowChanges”对象来实现修改,该对象存在于工作流的“System.Workflow.ComponentModel”命名空间里。用户可以通过“GetWorkflowDefinition()”方法得到工作流中活动的集合,并定位到工作流的根节点活动上。
WorkflowChanges workflowchange=new WorkflowChanges(workflowInstance.GetWorkflowDefinition());
接下来通过工作流实例的“TransientWorkflow()”方法将工作流中所有活动进行克隆,然后对克隆后的IfElse活动的属性进行修改。最后再将修改后的属性应用回工作流实例上。克隆出来的活动将保存在“CompositeActivity”类型的对象中。
CompositeActivity comactivity=workflowchanges.TransientWorkflow;
当以“声明性规则条件”方式定义逻辑判断条件时,如图4所示,WF会创建一个以“.rules”为扩展名的XML文件,判断条件”Condition1“的所有信息都在该文件中,该XML文件也就是动态修改的重点对象。
图4 workflow1.rules文件
“RuleDefinitions“类位于”System.WorkflowActivities”命名空间下,专门用于对“.rules”文件进行操作,用来获取XML文件中”RuleSet”和“RuleCondition”的信。用户可以通过以下代码对“Workflow1.rules”文件进行读取。
RuleDefinitions ruleDefinitions = (RuleDefinitions)comactivity.GetValue(RuleDefinitions.RuleDefinitionsProperty);
RuleConditionCollection conditions = ruleDefinitions.Conditions;
接下来就是从“conditions”对象中获取具体的“Condition1”逻辑判断的信息。“Condition1”以“Declarative Rule Condition”方式定义判断条件的名称。
RuleExpressionCondition condition1 = (RuleExpressionCondition)conditions["条件1"];
由于判断条件的信息都是存放在“Workflow1.Rules”文件中,对其分析会发现所需操作的XML节点是“CodeBinaryOperatorExpression”类型的。
所以用户可以通过以下代码对工作流IfElse活动控件的逻辑判断条件进行修改。
(condition1.Expression as CodeBinaryOperatorExpression).Right = new CodePrimitiveExpression(Convert.ToInt32(this.textBox1.Text));
最后只需要将修改后的信息更新应用回工作流的实例就可以实现动态修改功能了。
wfInstance.ApplyWorkflowChanges(wfchange);
通过以上步骤实现了对工作流中基于规则模式的活动所定义的判断条件进行动态修改,至此Windows上的应用程序项目就开发完毕,完整代码如下。
public partial class Form1 : Form
{
private WorkflowRuntime wfRuntime = null;
private WorkflowInstance wfInstance = null;
public Form1()
{
InitializeComponent();
wfRuntime = new WorkflowRuntime();
wfRuntime.StartRuntime();
}
private void button1_Click(object sender, EventArgs e)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("Condition", Convert.ToInt32(this.textBox2.Text));
Type wftype = typeof(Workflow1);
wfInstance = wfRuntime.CreateWorkflow(wftype, parameters);
WorkflowChanges wfchange = new WorkflowChanges(wfInstance.GetWorkflowDefinition());
CompositeActivity comactivity = wfchange.TransientWorkflow;
RuleDefinitions ruleDefinitions = (RuleDefinitions)comactivity.GetValue(RuleDefinitions.RuleDefinitionsProperty);
RuleConditionCollection conditions = ruleDefinitions.Conditions;
RuleExpressionCondition condition1 = (RuleExpressionCondition)conditions["条件1"];
(condition1.Expression as CodeBinaryOperatorExpression).Right = new CodePrimitiveExpression(Convert.ToInt32(this.textBox1.Text));
wfInstance.ApplyWorkflowChanges(wfchange);
wfInstance.Start();
}
}
示例5
1.3 动态添加StateActivity活动
在状态机工作流中同样可以动态加载或修改原有的业务流程。示例模拟一个人入党申请的流程,并在原有的基础上增加了“介绍人意见”的状态活动,示例的具体内容如下。
1.3.1 创建接口项目
根据示例的业务逻辑,在接口项目中定义了3个事件分别对应“入党申请”、“党委会审批”和“介绍人意见”。
[ExternalDataExchange]
public interfalce IClass
{
event EventHandler<ExternalDataEventArgs> AdmittedIntoParty;
event EventHandler<ExternalDataEventArgs> SponsorCommit;
event EventHandler<ExternalDataEventArgs> PCApproved;
}
1.3.2 创建工作流项目
创建一个状态机工作流库,如图1所示,分别添加3个“State”活动,并将它们命名为“提交入党申请书”、“党委会审批”和“结束”。然后将“提交入党申请书”状态设置为状态机工作流的初始状态,将“结束”设置为工作流的终止状态。最后为“提交入党申请书”和“党委会审批”状态分别添加一个EventDriven子活动,并建立状态的变迁关系。