在宿主应用程序程序中检索工作流数据
上节中,我们已经创建好了外部服务类和工作流。现在试着在宿主应用中创建和启用工作流,并接收来自工作流的消息和数据。
界面设计如下图,具体上节已有描述:
同样复制WorkflowFactory类到项目中以复用。
在宿主中关键要做的是勾住MVDataUpdate事件以便使应用程序知道接收数据的时间。
在“RetrieveMV Data”的点击响应函数中,加如下代码:
MVDataService.WorkflowMVDataServicedataService =
MVDataService.WorkflowMVDataService.CreateDataService(
_workflowInstance.InstanceId,
_workflowRuntime);
dataService.MVDataUpdate+= new EventHandler<MVDataService.MVDataAvailableArgs>(dataService_MVDataUpdate);
定义事件处理函数dataService_MVDataUpdate函数:(略,见附件代码),捕捉了数据获取完成事件并填充了ListView控件。
用InvokeWorkflow调用外部工作流
InvokeWorkflow可用来在正在执行的工作流中启动另一个工作流。
可按如下方法做一个例子:
新建一个顺序工作流库命名为“Workflow1”。在工作流设计器上拖一个Code活动, 可令它输出一个提示信息。同样新建一个顺序工作流库命名为Workflow2。在工作流器上上拖一个Code活动并令它输出另一个信息。编译Workflow2,并为Workflow1添加对它的引用。
回到Workflow1的设计器上,拖一个InvokeWorkflow活动到设计器上。这个新活动的有一个属性“TargetWorkflow”,点击它的浏览弹出“浏览和选择一个.NET类型”的对话框,选择Workflow2。
同样建立宿主应用。添加如下代码启动Workflow1:
WorkflowInstanceinstance = workflowRuntime.CreateWorkflow(typeof(Workflow1.Workflow1));
Instance.Start();
为宿主应用添加工作流完成事件的处理程序代码如下,以使两个工作流在完成时通知我们:
if(e.WorkflowDefinition is Workflow1.Workflow1)
{
Console.WriteLine("Workflow1 completed.");
}
else
{
Console.WriteLine("Workflow2 completed.");
}
要设置AutoResetEvent,以便强制应用程序等待工作流完成。编译运行如下图,输出信息的顺序可能会有所不同:
逻辑流活动
WF提供了基于运行时的条件进行逻辑处理控制流的活动。包括IfElse活动、While活动和Replicator活动。
对于条件的处理既可以依靠CodeCondition处理,也可使用RuleCondition,后者使用了WF的基于规则的处理方式。
示例:Qustioner应用程序
本示例依然是一个Windows Form应用程序。它会回答你三个问题,问题内容可修改,你可指定这三个问题是各自独立还是相互关联。
当工作流开始执行时你要把这些问题和相关的情况传入工作流。相互关联的问题只有在前面的问题回答正确时才被进一步提出;各自独立的问题要求你必须回答。
按照上一章的模式,我们需要生成一个活动以实现宿主应用和工作流之间的通信。创建项目命名为QuestionService活动。创建过程不再赘述,项目的结构与之前的类似,我们把精力集中在实现工作流上。
使用IfElse活动实现
IfElse活动要求你提供一个条件表达式,它其实是一个EventHandler,有一个ConditionalEventArgs参数,有一个Boolean类型的Result属性。IfElse活动根据Result的值来选择执行分支的哪一支,两个分支都可作为其他活动的容器。
在QuestionService项目中已使用工具生成了一个通信活动:SendResponseDataToHost。
工作流项目被命名为QuestionFlow,在设计视图上,拖一个IfElse活动。在其Condition属性设置为代码条件,展开后 输入AskQuestion1。拖两个Code活动到左右两个分支,左边的ExcudeCode设置为AffirmQ1,右边的设置为NegateQ1。
工作流类需要设置如下字段和属性包含三个问题,作为启动工作流时的参数:
privatestring[] _questions = null;
publicstring[] Questions
{
get{ return_questions; }
set{ _questions = value; }
}
添加Dependent属性标记这些问题是否关联:
privatebool_dependent = true;
publicboolDependent
{
get{return_dependent; }
set{_dependent = value; }
}
回答的结果应作如下存储:
private bool[] _response = null;
它的初始化在构造器里进行:
_response =new bool[3];
_response[0]= false;
_response[1]= false;
_response[2]= false;
AskQuestion1事件处理添加如下代码:
DialogResultresult = MessageBox.Show(Questions[0], "Questioner:",
MessageBoxButtons.YesNo,MessageBoxIcon.Question);
e.Result =(result == DialogResult.Yes);
NegateQ1添加如下代码:
_response[0]= false;
if(Dependent)
{
// Negateremaining answers.
_response[1]= false;
_response[2]= false;
}
AffirmQ1添加:
_response[0]= true;
以上就完成了对第一个问题的回答。对于第2、3个问题同样加两个IfElse活动进去。事件处理程序分别为AskQuestion2、NegateQ2、AffirmQ2…。
AskQuestionQ2的代码如下:
if(_response[0]== false&& Dependent)
{
// No need toask!
e.Result =false;
}
else
{
// Ask thequestion!
DialogResultresult = MessageBox.Show(Questions[1], "Questioner:",
MessageBoxButtons.YesNo,MessageBoxIcon.Question);
e.Result =(result == DialogResult.Yes);
}
NegateQ2如下:
_response[1]= false;
if(Dependent)
{
// Negateremaining answer
_response[2]= false;
}
AffirmQ2如下:
_response[1]= true;
同样,AskQuesionQ3代码如下:
if(_response[1]== false&& Dependent)
{
// No need toask!
e.Result =false;
}
else
{
// Ask thequestion!
DialogResultresult = MessageBox.Show(Questions[2], "Questioner:",
MessageBoxButtons.YesNo,MessageBoxIcon.Question);
e.Result =(result == DialogResult.Yes);
NegateQ3代码如下:
_response[2]= false;
AffirmQ3代码如下:
_response[2]= true;
拖SendResponseDataToHost活动到工作流设计器上第三个IfEsle下边。把它的response属性设置为三个False(点浏览按钮后弹出的Boolean集合编辑器中设置)。
还需要把保存了问题问答结果的数组的值和SendResponseDataToHost活动联系起来。我们拖一个Code活动到第三个IfElse活动和SendResponseDatToHost之间。把它的ExecuteCode属性设置为CopyResponse,其代码如下:
sendResponseDataToHost1.responses= _response;
好了,编译运行。
使用While活动实现
上例如果使用While活动实现会更好些。我们将使用While活动替换上例。同样它也使用ConditionalEventArgs来返回你的判断结果,如果Result属性为true继续循环,false则终止循环。
在工作流设计视图中拖一个While活动。在它的Condtion属性中输入TestComplete。
拖一个Code活动到WhileActivity1中,把它的ExecuteCode属性值设为AskQuestion。
把SendResponseDataToHost活动拖入放在While活动下。
同样需要设置上例中的一些字段和属性,包括存储问题的数组和属性,是否依赖的字段和属性以及回答的数组和属性。
添加如下代码:
private Int32_qNum = 0;
TestComplete事件处理程序添加如下代码:
if(_qNum>= Questions.Length)
{
// Assignoutgoing data.
sendResponseDataToHost1.responses= _response;
// Done, soexit loop.
e.Result =false;
}
else
{
// Not done,so continue loop.
e.Result =true;
}
在AskQuestion事件处理程序中添加如下代码:
DialogResultresult = MessageBox.Show(Questions[_qNum], "Questioner:",
MessageBoxButtons.YesNo,MessageBoxIcon.Question);
_response[_qNum]= (result == DialogResult.Yes);
// Checkresponse versus dependency
if(!_response[_qNum]&& Dependent)
{
// Negateremaining questions
while(_qNum< Questions.Length)
{
// Negatethis question
_response[_qNum]= false;
// Next question
++_qNum;
}
} // if
else
{
// Set up fornext iteration
++_qNum;
}
同样放一个Code活动到While活动下把问题结果数组传给SendResponseDataToHost活动。编译执行。